美团接口自动化测试实践

1、概述

1.1 接口自动化概述

众所周知,接口自动化测试有着以下特色:前端

  • 低投入,高产出。
  • 比较容易实现自动化。
  • 和UI自动化测试相比更加稳定。

如何作好一个接口自动化测试项目呢?java

我认为,一个“好的”自动化测试项目,须要从“时间”“人力”“收益”这三个方面出发,作好“取舍”。mysql

不能因为被测系统发生一些变动,就致使花费了几个小时的自动化脚本没法执行。同时,咱们须要看到“收益”,不能为了总想看到100%的成功,而少作或者不作校验,可是校验多了维护成本必定会增多,可能天天都须要进行大量的维护。git

因此作好这三个方面的平衡并不容易,常常能看到作自动化的同窗,作到最后就本末倒置了。github

 

1.2 提升ROI

想要提升ROI(Return On Investment,投资回报率),咱们必须从两方面入手:web

  1. 减小投入成本。
  2. 增长使用率。

 

针对“减小投入成本”

咱们须要作到:正则表达式

  • 减小工具开发的成本。尽量的减小开发工具的时间、工具维护的时间,尽量使用公司已有的,或是业界成熟的工具或组件。
  • 减小用例录入成本。简化测试用例录入的成本,尽量多的提示,若是能够,开发一些批量生成测试用例的工具。
  • 减小用例维护成本。减小用例维护成本,尽可能只用在页面上作简单的输入便可完成维护动做,而不是进行大量的代码操做。
  • 减小用例优化成本。当团队作用例优化时,能够经过一些统计数据,进行有针对性、有目的性的用例优化。

 

针对“增长使用率”

咱们须要作到:sql

  • 手工也能用。不仅是进行接口自动化测试,也能够彻底用在手工测试上。
  • 人人能用。每个须要使用测试的人,包括一些非技术人员均可以使用。
  • 当工具用。将一些接口用例当成工具使用,好比“生成订单”工具,“查找表单数据”工具。
  • 天天测试。进行每日构建测试。
  • 开发的在构建以后也能触发测试。开发将被测系统构建后,能自动触发接口自动化测试脚本,进行测试。

因此,我这边开发了Lego接口测试平台,来实现我对自动测试想法的一些实践。先简单浏览一下网站,了解一下大概是个什么样的工具。数据库

首页:
imagejson

用例维护页面:
image

自动化用例列表:
image

在线执行结果:
image

用例数量统计:
image

 

1.3 Lego的组成

Lego接口测试解决方案是由两部分组成的,一个就是刚刚看到的“网站”,另外一个部分就是“脚本”。

下面就开始进行“脚本设计”部分的介绍。

 

2、脚本设计

2.1 Lego的作法

Lego接口自动化测试脚本部分,使用很常见的Jenkins+TestNG的结构。

image

相信看到这样的模型并不陌生,由于不少的测试都是这样的组成方式。

将自动化测试用例存储至MySQL数据库中,作成比较常见的“数据驱动”作法。

不少团队也是使用这样的结构来进行接口自动化,沿用的话,那在之后的“推广”中,学习和迁移成本低都会比较低。

 

2.2 测试脚本

首先来简单看一下目前的脚本代码:

public class TestPigeon { String sql; int team_id = -1; @Parameters({"sql", "team_id"}) @BeforeClass() public void beforeClass(String sql, int team_id) { this.sql = sql; this.team_id = team_id; ResultRecorder.cleanInfo(); } /** * XML中的SQL决定了执行什么用例, 执行多少条用例, SQL的搜索结果为须要测试的测试用例 */ @DataProvider(name = "testData") private Iterator<Object[]> getData() throws SQLException, ClassNotFoundException { return new DataProvider_forDB(TestConfig.DB_IP, TestConfig.DB_PORT, TestConfig.DB_BASE_NAME,TestConfig.DB_USERNAME, TestConfig.DB_PASSWORD, sql); } @Test(dataProvider = "testData") public void test(Map<String, String> data) { new ExecPigeonTest().execTestCase(data, false); } @AfterMethod public void afterMethod(ITestResult result, Object[] objs) {...} @AfterClass public void consoleLog() {...} } 

image

有一种作法我一直不提倡,就是把测试用例直接写在Java文件中。这样作会带来不少问题:修改测试用例须要改动大量的代码;代码也不便于交接给其余同窗,由于每一个人都有本身的编码风格和用例设计风格,这样交接,最后都会变成由下一个同窗所有推翻重写一遍;若是测试平台更换,没法作用例数据的迁移,只能手动的一条条从新输入。

因此“测试数据”与“脚本”分离是很是有必要的。

网上不少的范例是使用的Excel进行的数据驱动,我这里为何改用MySQL而不使用Excel了呢?

在公司,咱们的脚本和代码都是提交至公司的Git代码仓库,若是使用Excel……很显然不方便平常常常修改测试用例的状况。使用MySQL数据库就没有这样的烦恼了,因为数据与脚本的分离,只需对数据进行修改便可,脚本每次会在数据库中读取最新的用例数据进行测试。同时,还能够防止一些操做代码时的误操做。

这里再附上一段我本身写的DataProvider_forDB方法,方便其余同窗使用在本身的脚本上:

import java.sql.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 数据源 数据库
 *
 * @author yongda.chen
 */
public class DataProvider_forDB implements Iterator<Object[]> {

    ResultSet rs;
    ResultSetMetaData rd;

    public DataProvider_forDB(String ip, String port, String baseName, 
        String userName, String password, String sql) throws ClassNotFoundException, SQLException {

        Class.forName("com.mysql.jdbc.Driver");
        String url = String.format("jdbc:mysql://%s:%s/%s", ip, port, baseName);
        Connection conn = DriverManager.getConnection(url, userName, password);
        Statement createStatement = conn.createStatement();

        rs = createStatement.executeQuery(sql);
        rd = rs.getMetaData();
    }

    @Override
    public boolean hasNext() {
        boolean flag = false;
        try {
            flag = rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return flag;
    }

    @Override
    public Object[] next() {
        Map<String, String> data = new HashMap<String, String>();
        try {
            for (int i = 1; i <= rd.getColumnCount(); i++) {
                data.put(rd.getColumnName(i), rs.getString(i));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        Object r[] = new Object[1];
        r[0] = data;
        return r;
    }

    @Override
    public void remove() {
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

2.3 配置文件

上面图中提到了“配置文件”,下面就来简单看一下这个XML配置文件的脚本:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Pigeon Api测试" parallel="false"> <test name="xxx-xxx-service"> <parameter name="sql" value="SELECT * FROM API_PigeonCases WHERE team_id=2 AND isRun=1 AND service='xxx-xxx-service' AND env='beta';"/> <classes> <class name="com.dp.lego.test.TestPigeon"/> </classes> </test> <listeners> <listener class-name="org.uncommons.reportng.HTMLReporter"/> <listener class-name="org.uncommons.reportng.JUnitXMLReporter"/> </listeners> </suite> 

image

对照上图来解释一下配置文件:

  • SQL的话,这里的SQL主要决定了选取哪些测试用例进行测试。
  • 一个标签,就表明一组测试,能够写多个标签。
  • “listener”是为了最后可以生成一个ReportNG的报告。
  • Jenkins来实现每日构建,可使用Maven插件,经过命令来选择须要执行的XML配置。

 

这样作有什么好处呢?

使用SQL最大的好处就是灵活

image

如上面的这个例子,在数据库中会查询出下面这56条测试用例,那么这个标签就会对这56条用例进行逐一测试。

 

多标签时,能够分组展现

image

使用多个标签来区分用例,最大的好处就是也能在最后的报告上,达到一个分组展现的效果。

 

报告更美观丰富

image

因为使用了ReportNG进行报告的打印,因此报告的展现要比TestNG自带的报告要更加美观、而且能自定义展现样式,点开能看到详细的执行过程。

image

若是有执行失败的用例,一般报错的用例会在最上方优先展现。

 

支持多团队

image

当两个团队开始使用时,为了方便维护,将基础部分抽出,各个团队的脚本都依赖这个Base包,而且将Base包版本置为“SNAPSHOT版本”。使用“SNAPSHOT版本”的好处是,以后我对Lego更新,各个业务组并不须要对脚本作任何改动就能及时更新。

 

当更多的团队开始使用后,比较直观的看的话是这个样子的:

image

每一个团队的脚本都依赖于个人这个Base包,因此最后,各个业务团队的脚本就变成了下面的这个样子:

image

能够看到,使用了Lego以后:

  • 没有了Java文件,只有XML文件
  • xml中只须要配置SQL。
  • 执行和调试也很方便。
  • 能够右键直接执行想要执行的测试配置。
  • 可使用maven命令执行测试:
    • mvn clean test -U -Dxml=xmlFileName 。
    • 经过参数来选择须要执行的xml文件。
  • 也可使用Jenkins来实现定时构建测试。

因为,全部测试用例都在数据库因此这段脚本基本不须要改动了,减小了大量的脚本代码量。

有些同窗要问,有时候编写一条接口测试用例不仅是请求一下接口就行,可能还须要写一些数据库操做啊,一些参数可能还得本身写一些方法才能获取到啊之类的,那不code怎么处理呢?

下面就进入“用例设计”,我将介绍我如何经过统一的用例模板来解决这些问题。

 

3、用例设计

3.1 一些思考

我在作接口自动化设计的时候,会思考通用、校验、健壮、易用这几点。

 

通用

  • 简单、方便
    • 用例数据与脚本分离,简单、方便。
    • 免去上传脚本的动做,能避免不少没必要要的错误和维护时间。
    • 便于维护。
  • 模板化
    • 抽象出通用的模板,可快速拓展。
    • 数据结构一致,便于批量操做。
    • 专人维护、减小多团队间的重复开发工做。
    • 因为使用了统一的模板,那各组之间即可交流、学习、作有效的对比分析。
    • 若是之后这个平台再也不使用,或者有更好的平台,可快速迁移。
  • 可统计、可拓展
    • 可统计、可开发工具;如:用例数统计,某服务下有多少条用例等。
    • 可开发用例维护工具。
    • 可开发批量生成工具。

 

校验

在写自动化脚本的时候,都会想“细致”,而后“写不少”的检查点;但当“校验点”多的时候,又会由于不少缘由形成执行失败。因此咱们的设计,须要在保证充足的检查点的状况下,还要尽量减小误报。

  • 充足的检查点
    • 能够检查出被测服务更多的缺陷。
  • 尽可能少的误报
    • 能够减小不少的人工检查和维护的时间人力成本。
  • 还要
    • 简单、易读。
    • 最好使用一些公式就能实现本身想要的验证。
    • 通用、灵活、多样。
    • 甚至能够用在其余项目的检查上,减小学习成本。

 

健壮

执行测试的过程当中,不免会报失败,执行失败可能的缘由有不少,简单分为4类:

image

  • 被测系统出错,这部分实际上是咱们但愿看到的,由于这说明咱们的自动化测试真正地发现了一个Bug,用例发挥了它的价值,因此,这是咱们但愿看到的。
  • 测试工具出错,这部分实际上是咱们不但愿看到的,由于很大可能咱们今天的自动化至关于白跑了。
  • 测试数据错误,这是咱们要避免的,既然数据容易失效,那我在设计测试平台的时候,就须要考虑若是将全部的数据跑“活”,而不是只写“死”。
  • 不可抗力,这部分是咱们也很无奈的,可是这样的状况不多发生。

那针对上面的状况:

  • 参数数据失效
    • 支持实时去数据库查询。
    • 支持批量查。
  • IP进场发生变动
    • 自动更新IP。
  • 灵活、可复用
    • 支持批量维护。
    • 接口测试执行前生成一些数据。
    • 接口执行完成后销毁一些数据。
    • 支持参数使用另外一条测试用例的返回结果。
    • 支持一些请求参数实时生成,如token等数据,从而减小数据失效的问题。

经过这些手段,提升测试用例的健壮性,让每一条自动化测试用例都能很好的完成测试任务,真正发挥出一条测试用例的价值。

 

易用

  • 简单
    • 功能强大,但要人人会用。
    • 非技术人员也要会用。
  • 减小代码操做
    • 让自动化开发人员注意力能更多的放在用例自己,而不是浪费在可有可无的开发工做上面。
  • 还要
    • 配置能复用。
    • 通用、易学。
    • 一些数据能自动生成。

 

3.2 Lego接口自动化测试用例

说了这么多,那咱们来看一下一条Lego接口测试用例的样子。

一条Lego自动用例执行顺序大概是以下图这样:

image

简单区分一下各个部分,能够看到:

image

那上面图中提到了两个名词:

  • “参数化”
  • “先后置动做”

下面会先对这两个名词作一个简单的介绍。

 

3.3 参数化

好比一个请求须要用到的参数。

{
    "sync": false, "cityId": 1, "source": 0, "userId": 1234, "productId": 00004321 } 

这个例子中有个参数"productId": 00004321,而因为测试的环境中,表单00004321极可能一些状态已经发生了改变,甚至表单已经删除,致使接口请求的失败,那么这时候,就很适合对"productId": 00004321进行参数化,好比写成这样:

{
    "sync": false,
    "cityId": 1,
    "source": 0,
    "userId": 1234,
    "productId": ${myProductId}
}

因此对“参数化”简单的理解就是:

经过一些操做,将一个“值”替换掉测试用例里的一个“替代字符”

${myProductId} 的值能够经过配置获取到:

  • Key-Value
    • 配置 Value=00004321。
  • SQL获取
    • 执行一个select语句来实时查询获得可用ID。
  • 已有测试用例
    • 某个接口接口测试用例的返回结果。

 

“参数化”实例

下面咱们来看一个“参数化”的实例:

(1) 首先咱们在参数化维护页面中新建一个参数化,shopdealid

image

经过配置咱们能够看到这个参数的值,是执行了一条SQL后,取用执行结果中DealID字段的值。

(2) 在用例中,将须要这个表单号的地方用${shopdealid}替代。

image

那在编写测试用例的时候,你们能够看一下这个放大的图片,在这里的ProductID的值并非硬代码一个固定的表单号,而是选择了刚才配置的参数化数据。

(3) 执行结果中,${shopdealid} 变为实时查询数据库的来的一个真实的表单号。

image

从结果中能够看到,咱们的这个参数被替换成了一个有效的值,而这个值就是咱们刚刚配置的那个SQL实时查询而来的。

 

“参数化”的场景

多个测试用例使用同一个参数进行测试

如50条测试用例都使用同一个id做为参数进行测试,这时候咱们须要变动这个id。

无参数化时:

  • 须要修改50次,即每条测试用例中的id都得进行修改。
  • 可能会有遗漏。
    有参数化时:
  • id部分用 ${myID} 替代。
  • 须要修改的话,在“参数化维护”页面中维护 ${myID}这条数据就能够。修改一次,全部使用${myID}的用例都配置完成。

 

测试数据过时致使测试用例执行失败

如一条用例参数须要传入token,可是Token会由于时间问题而致使过时,这时候用例就失败了。

无参数化时:

  • 常常修改Token,或是写一段id转Token的代码。
  • 方法可能会重复编写。
  • 多个团队之间可能实现方式也不一样。

有参数化时:

  • 使用参数化工具,Lego统一管理。
  • 维护一个参数化 如:${测试用Token} = id:123

 

数据库获取有效测试数据

参数中须要传入DealId做为参数,写死参数的话,若是这个DealId被修改引发失效,那这条测试用例就会执行失败。

不使用Lego时:

  • 测试环境中,一个订单时常会由于测试须要被修改数据,致使单号失效,最后致使自动化失败。
  • 编写相关代码来作好数据准备工做。
  • 在代码中编写读取数据库的方法获取某些内容。

在Lego上的方案:

  • 使用参数化,实时获取sql结果,查询出一条符合条件的dealId来实现。
  • 使用参数化,调用写好的“生成订单”接口用例实现,拿单号来实现。
  • 先后置动做,插入一条知足条件的数据。

 

3.4 先后置动做

“先后置动做”的概念就比较好理解了:

在接口请求以前(或以后),执行一些操做

目前先后置动做支持6种类型:

  • 数据库SQL执行
    • 有时候在执行接口请求前,为了保证数据可用,可能须要在数据库中插入或删除一条信息,这时候就可使用先后置动做里的“执行SQL语句”类型,来编写在接口请求前(后)的 Insert 和 Delete 语句。
  • 已有测试用例执行
    • 好比当前测试用例的请求参数,须要使用另外一条测试用例的返回结果,这时候就可使用“执行测试用例”类型,写上Lego上某条测试用例的ID编号,就能够在当前用例接口请求前(后)执行这条测试用例。
    • 先后置动做中测试用例的返回结果能够用于当前用例的参数,对测试用例返回结果内容的获取上,也支持JsonPath和正则表达式两种方式。
  • MQ消息发送
    • 在接口请求前(后)发送MQ消息。
  • HTTP请求
  • 等待时间
  • 自定义的Java方法
    • 若是上面的方法还知足不了需求,还能够根据本身的须要,编写本身的Java方法。
    • 能够在Lego-Kit项目中,编写本身须要的Java方法,选择“执行Java方法”,经过反射实现自定义Java方法的执行。

这里的SQL同时支持Select操做,这里其实也是作了一些小的设计,会将查询出来的所有的结果,放入到这个全局Map中。 

好比查询一条SQL获得下表中的结果:

id    name       age       number   
0 张三 18 1122
1 李四 30 3344

那咱们可使用下面左边的表达式,获得对应的结果:

  • ${pre.name} ---- 获得 “张三”å
  • ${pre.age} ---- 获得 18
  • ${pre.number} ---- 获得 1122

也能够用:

  • ${pre.name[0]} ---- 获得 “张三”
  • ${pre.age[0]} ---- 获得 18
  • ${pre.number[0]} ---- 获得 1122
  • ${pre.name[1]} ---- 获得 “李四”
  • ${pre.age[1]} ---- 获得 30
  • ${pre.number[1]} ---- 获得 3344

这样的设计,更加帮助在用例设计时,提供数据准备的操做。

 

“先后置动做”实例

(1) 首先咱们在先后置维护页面中新建一个动做,获取库存上限未卖光团单 。

image

这个配置也是能够支持在线调试的,在调试中,能够看到可使用的参数化:

image

(2) 在测试用例中的前置动做,添加获取库存上限未卖光团单 。

image

这样就能够在整个测试用例中,使用${pre.ProductID},来替换掉原有的数据信息。

(3) 最后请求接口,返回了执行成功 。 

image

Q & A

Q:那若是一样是获取三个参数,使用3个“参数化的Select操做”和使用1个“前置动做的Select操做”又有什么不一样呢?

A: 不一样在于执行时间上。
好比,咱们查询最新的有效团单的“单号”“下单人”和“手机号”三个字段。
使用3个“参数化的Select操做”:可能当执行${单号}的时候获得的订单号是“10001”,可是当执行到${下单人}的时候,可能有谁又下了一单,可能取到的下单人变成了“10002”的“李四”而不是“10001”的“张三”了,最后可能“单号”“下单人”和“手机号”三个字段去的数据并不是同一行的数据。
而使用“前置动做的Select操做”:就能够避免上面的问题,由于全部字段的数据是一次性查询出来的,就不会出现错位的状况。

Q : 那“参数化的Select操做”和“前置动做的Select操做”这样不一样的取值时机又有什么好用之处呢?

A : 因为“前置动做”必定是接口请求前执行,“参数化”必定是用到的时候才执行这样的特性。
因此在检查点中,若是要验证一个数据库字段在通过接口调用后发生了变动,那使用“前置动做”和“参数化”同时去查询这个字段,而后进行比较,不一致就说明发生了变化。
因此根据使用场景,选择合适的参数化方式,很重要,选择对了,能大大提高测试用例的测试数据健壮性。

 

3.5 执行各部分

回到一开始的流程图,能够按照一类一类来看执行过程。

 

测试发起

image

测试发起基本仍是使用的Jenkins,稳定、成熟、简单、公司工具组支持,也支持从Lego的Web页面进行执行操做。

 

数据 / 环境准备

image

使用 @DataProvider 的方式,从DB数据库中读取测试用例,逐一执行进行测试。

 

测试执行

image

在正式执行测试用例以前,会先进行一波参数替换的动做,在调用接口以后,还会执行一次参数替换动做。

image

参数替换后会进行前置动做的执行,而后在调用接口以后还会执行测试后动做,最后执行后置动做。

image

接口请求这部分就没什么好说的了,就是经过接口请求的参数,请求对应的接口,拿到返回结果。

这里的话是为了方便通用,因此要求返回的结果都是使用的String类型。这样作最大的好处就是。好比说我如今有一种新的接口类型须要接入。那只须要写一个方法可以请求到这个接口,而且拿到String类型的返回结果,就能够很快将新的接口类型接入Lego测试平台进行接口测试。 

 

检查点校验

image

检查点部分是一条自动化测试用例的精髓,一条自动化测试用例是否能真正的发挥它的测试功能,就是看QA对这条测试用例的检查点编写是否作了良好设计。在Lego平台上,目前我拥有的检查点有6种不一样的类型。

  • 异常检查点
    • 当返回结果为异常时,则会报错。
    • 可是有时候为了作异常测试,能够将这个检查点关掉。
  • 不为空检查点
    • 顾名思义,当出现""、"[]"、"{}"、null 这样的的结果,都会报错。也能够根据本身用例的实际状况关闭。
  • 包含检查点
  • 不包含检查点
    • “包含”和“不包含”检查点是将接口的返回结果做为一个String类型来看,检查全部返回内容中是否“包含”或“不包含”指定的内容。
  • 数据库参数检查点
    • 顾名思义,不作过多的解释了。
  • JsonPath检查点
    • 这是我在Lego上设计的最具备特点的一种检查点类型。

JsonPath的基本写法是{JsonPath语法}==value

JsonPath的语法和XPath的语法差很少,都是根据路径的方法找值。这里也是主要是针对返回结果为JSON数据的结果,进行检查。

具体的JsonPath语法能够参考:https://github.com/json-path/JsonPath

说完了"JsonPath的语法",如今说一下"JsonPath检查点的语法""JsonPath检查点的语法"是我本身想的,主要针对如下几种数据类型进行校验:

 

(1) 字符串类型结果检验

  • 等于:==
  • 不等于:!==
  • 包含:=
  • 不包含:!=

例如:

  • {$.[1].name}==aa:检查返回的JSON中第2个JSON的name字段是否等于aa。
  • {$..type}=='14':检查返回的JSON中每个JSON的name字段是否等于aa。
  • {$.[1].type}==14 && {$.[1].orderId}==106712:一条用例中多个检查用&&链接。
  • {$..orderId}!==12:检查返回的JSON中每一个JSON的orderId字段是否不等于12。
  • {$..type}=1:检查返回的JSON中每一个JSON的type字段是否包含1。
  • {$.[1].type}!=chenyongda:检查返回的JSON中第2个JSON的type字段是否不包含chenyongda。

 

(2) 数值校验

  • 等于:=
  • 大于:>
  • 大于等于:>=
  • 小于:<
  • 小于等于:<=

例如:

  • {$.[0].value}<5:检查返回的JSON中第1个JSON的value字段的列表是否小于3。
  • {$.[1].value}>4:检查返回的JSON中第2个JSON的value字段的列表是否大于4。

 

(3) List结果检验

  • list长度:.length
  • list包含:.contains(param)
  • list成员:.get(index)

例如:

  • {$..value}.length=3:检查返回的JSON中每一个JSON的value字段的列表是否等于3。
  • {$.[0].value}.length<5:检查返回的JSON中第1个JSON的value字段的列表是否小于3。
  • {$.[1].value}.length>4:检查返回的JSON中第2个JSON的value字段的列表是否大于4。
  • {$..value}.contains('222'):检查返回的JSON中每一个JSON的value字段的列表是否包含222字符串。
  • {$.[0].value}.contains(1426867200000):检查返回的JSON中第1个JSON的value字段的列表是否包含1426867200000。
  • {$.[0].value}.get(0)=='222':检查返回的JSON中第1个JSON的value字段的列表中第1个内容是否等于222。
  • {$..value}.get(2)='22':检查返回的JSON中每一个JSON的value字段的列表中第3个内容是否包含22。

 

(4) 时间类型处理

时间戳转日期时间字符串:.todate

例如:

  • {$..beginDate}.todate==2015-12-31 23:59:59:检查返回的JSON中beginDate这个时间戳转换成日期后是否等于2015-12-31 23:59:59。

 

当JsonPath返回的结果是列表的形式时

检查点 检查点等号左边 指望值 验证效果
{$.value}=="good" ['good', 'good', 'bad', 'good'] "good" 做为4个检查点,会拿列表里的每一个对象逐一和“指望值”进行检验,每一次对比都是一个独立的检查点。
{$.value}==["good"] ['good', 'good', 'bad', 'good'] ["good"] 做为1个检查点,做为一个总体作全量比对。
{$.value}==['a', 'b'] [['a', 'b'],['a', 'b'],['a', 'b', 'c']] ['a', 'b'] 做为3个检查点,道理和1同样,列表中的数据分别和指望值作比较。

 

除此以外,还有很是多的花样玩法

JsonPath中的检查支持“参数化”和“先后置动做”,因此会看到不少如:

{$.param}='${param}' && {$.param}==${pre.param}

这样的检查点:

“参数化”和“先后置动做”也支持递归配置,这些都是为了可以让接口自动化测试用例写的更加灵活好用。

 

测试结果

image

使用ReportNG能够打印出很漂亮的报告。

报告会自定义一些高亮等展现方式,只须要在ReportNG使用前加上下面的语句,就能够支持“输出逃逸”,可以使用HTML标签自定义输出样式。

System.setProperty("org.uncommons.reportng.escape-output", "false"); 

 

后期优化

image

当使用Jenkins执行后,经过Jenkins API 、和Base包中的一些方法,定时获取测试结果,落数据库,提供生成统计图表用。

 

4、网站功能

4.1 站点开发

既然打算作工具平台了,就得设计方方面面,惋惜人手和时间上的不足,只能我一人利用下班时间进行开发。也算是担任了Lego平台的产品、后端开发、前端开发、运维和测试等各类角色。

Jenkins+TestNG+ReportNG+我本身开发的基本接口自动化测试Base jar包,基本上没什么太大难度。可是站点这块,在来美团以前,还真没开发过这样的工具平台,这个算是个人第一个带Web界面的工具。边Google边作,没想到不久还真的架起来了一个简易版本。

使用 Servlet + Jsp 进行开发,前端框架使用Bootstrap,前端数据使用jstl,数据库使用MySQL,服务器使用的公司的一台Beta环境Docker虚拟机,域名是申请的公司内网域名,并开通北京上海两侧内网访问权限。

功能上基本都是要知足的,界面上,虽然作不到惊艳吧,可是绝对不能丑,功能知足,可是长得一副80年代的界面,我本身都会嫌弃去使用它,因此界面上我仍是花了一些时间去调整和设计。熟练之后就快多了。

 

4.2 总体组成

image

目前Lego由五个不一样的项目组成,分别是“测试脚本”、“Lego-web页面项目”、“用于执行接口测试的base包”、“小工具集合Lego-kit”和“lego-job”,经过上图能够看出各项目间的依赖关系。

细化各个项目的功能,就是下图:

image

简单来讲,网站部分和脚本是分离的,中间的纽带是数据库。因此,没有网站,脚本执行一点问题也没有;一样的,网站的操做,和脚本也没有关系。

 

4.3 使用-平常维护

Step 1

image

天天上班来会收到这样的测试邮件,经过邮件能知道昨晚执行的状况。若是有报错,能够点击“详细报告连接”,跳转到在线报告。

 

Step 2

image

在现报告能够直接看到执行报错的信息,而后点击“LEGO维护传送门”,能够跳转到Lego站点上,进行用例维护。

 

Step 3

跳转到站点上之后,能够直接展现出该条测试用例的全部信息。定位,维护、保存,维护用例,能够点击“执行”查看维护后的执行结果,维护好后“保存”便可。

仅仅3步,1~2分钟便可完成对一条执行失败的用例进行定位、调试和维护动做。

 

4.4 用例编辑

image

经过页面,咱们就能够对一条测试用例进行:

  • 新建
  • 复制
  • 编辑
  • 删除
  • 是否放入每日构建中进行测试

 

4.5 在线调试

image

lego-web项目一样的使用base进行的用例执行,因此执行结果和打印都与脚本执行的一致的。

 

4.6 用例生成工具

为了更方便的写用例,针对部分接口开发了一键批量生成用例的小工具。

 

4.7 执行结果分析

经过Jenkins接口、Base包中基础Test方法,将结果收集到数据库,便于各组对测试结果进行分析。

image

这是天天执行后成功率走势图:

image

也能够按月进行统计,生成统计的图表,帮助各个团队进行月报数据收集和统计。

 

4.8 失败缘由跟踪

有了能直观看到测试结果的图表,就会想要跟踪失败缘由。

image

因此在成功率数据的右边,会有这样的跟踪失败缘由的入口,也能够很直观地看到哪一些失败的缘由尚未被跟踪。点开后能够对失败缘由进行记录。

image

最后会有生成图表,能够很清晰地看到失败缘由以及失败类型的占比。

 

4.9 代码覆盖率分析

结合Jacoco,咱们能够对接口自动化的代码覆盖率进行分析。

image

在多台Slave机器上配置Jacoco仍是比较复杂的,因此能够开发覆盖率配置辅助工具来帮助测试同窗,提升效率。 

image

 

4.10 用例优化方向

除了上面的图表,还会给用例优化提供方向。

image
经过用例数量统计的图表,咱们能够知道哪些服务用例还比较少,哪些环境的用例还比较少,能够比较有针对性的进行测试用例的补充。

image
经过失败缘由的图表,咱们能够改善本身用例中的“参数化”和“先后置动做”的使用,增长测试用例的健壮性。

image
经过线上接口调用量排序的图表。咱们能够有效的知道优先维护哪些服务的测试用例,经过表格中,咱们能够看到,哪些服务已经覆盖了测试用例,哪些没有被覆盖, 给各组的QA制定用例开发计划,提供参考。

image
同时在维护接口自动化测试的时候,都会看到用例评分的状况,来协助QA提升用例编写的质量。

 

4.11 收集反馈/学习

还作了“需求白板”,用来收集使用者的需求和Bug。除此以外,Lego平台已经不仅是一个接口测试的平台,还可让想学习开发的QA领任务,学习一些开发技巧,提升本身的代码能力。

 

5、总结

  1. 为了减小开发成本,使用比较常见的Jenkins+TestNG的脚本形式。
  2. 为了简化code操做,使用DB进行测试用例存储,并抽象出用例摸版。
  3. 为了减低新建用例成本,开发“用例维护页面”和“一键生成”等工具。
  4. 为了减低维护成本,加跳转连接,维护一条用例成本在几分钟内。
  5. 为了增长用例健壮性,设计了“参数化”、“先后置动做”等灵活的参数替换。
  6. 为了易用和兼容,统一“返回结果”类型,统一“检查点”的使用。
  7. 为了接口自动化用例设计提供方向,结合Jacoco作代码覆盖率统计,并开发相关配置工具
  8. 为了便于分析数据,从DOM、CAT、Jenkins上爬各类数据,在页面上用图表展现。
  9. 为了优化用例,提供“用例打分”、“线上调用量排行”等数据进行辅助。
相关文章
相关标签/搜索