Lego- 美团接口自动化测试实践
https://www.infoq.cn/article/G8CN7zGCqUeVhRr1KE8x?utm_source=related_read_bottom&utm_medium=article
一、概述
1.1 接口自动化概述
众所周知,接口自动化测试有着如下特点:
-
低投入,高产出。
-
比较容易实现自动化。
-
和 UI 自动化测试相比更加稳定。
如何做好一个接口自动化测试项目呢?
我认为,一个“好的”自动化测试项目,需要从 “时间”、“人力”、“收益” 这三个方面出发,做好“取舍”。
不能由于被测系统发生一些变更,就导致花费了几个小时的自动化脚本无法执行。同时,我们需要看到“收益”,不能为了总想看到 100%的成功,而少做或者不做校验,但是校验多了维护成本一定会增多,可能每天都需要进行大量的维护。
所以做好这三个方面的平衡并不容易,经常能看到做自动化的同学,做到最后就本末倒置了。
1.2 提高 ROI
想要提高 ROI(Return On Investment,投资回报率),我们必须从两方面入手:
-
减少投入成本。
-
增加使用率。
针对“减少投入成本”
我们需要做到:
-
减少工具开发的成本。 尽可能的减少开发工具的时间、工具维护的时间,尽可能使用公司已有的,或是业界成熟的工具或组件。
-
减少用例录入成本。 简化测试用例录入的成本,尽可能多的提示,如果可以,开发一些批量生成测试用例的工具。
-
减少用例维护成本。 减少用例维护成本,尽量只用在页面上做简单的输入即可完成维护动作,而不是进行大量的代码操作。
-
减少用例优化成本。 当团队做用例优化时,可以通过一些统计数据,进行有针对性、有目的性的用例优化。
针对“增加使用率”
我们需要做到:
-
手工也能用。 不只是进行接口自动化测试,也可以完全用在手工测试上。
-
人人能用。 每一个需要使用测试的人,包括一些非技术人员都可以使用。
-
当工具用。 将一些接口用例当成工具使用,比如“生成订单”工具,“查找表单数据”工具。
-
每天测试。 进行每日构建测试。
-
开发的在构建之后也能触发测试。 开发将被测系统构建后,能自动触发接口自动化测试脚本,进行测试。
所以,我这边开发了 Lego 接口测试平台,来实现我对自动测试想法的一些实践。先简单浏览一下网站,了解一下大概是个什么样的工具。
![](https://static001.infoq.cn/resource/image/63/b7/63c39c2d9256c7f87f1a2043bb06e5b7.png)
首页
![](https://static001.infoq.cn/resource/image/dd/ea/dd693408670abfd34dca99f809541aea.png)
用例维护页面
![](https://static001.infoq.cn/resource/image/48/bf/48bd0d880067988c517eb43564cf6cbf.png)
自动化用例列表
![](https://static001.infoq.cn/resource/image/af/cf/afc2fa817bfb55a1d9aac9df5e91b0cf.png)
在线执行结果
![](https://static001.infoq.cn/resource/image/c2/b6/c222df030b67f56dd5d805226124dcb6.png)
用例数量统计
1.3 Lego 的组成
Lego 接口测试解决方案是由两部分组成的,一个就是刚刚看到的“网站”,另一个部分就是“脚本”。
下面就开始进行“脚本设计”部分的介绍。
二、脚本设计
2.1 Lego 的做法
Lego 接口自动化测试脚本部分,使用很常见的 Jenkins+TestNG 的结构。
![](https://static001.infoq.cn/resource/image/d9/b8/d9f48fcc8fda32a87f28382f3db552b8.png)
Jenkins+TestNG 的结构
相信看到这样的模型并不陌生,因为很多的测试都是这样的组成方式。
将自动化测试用例存储至 MySQL 数据库中,做成比较常见的 “数据驱动” 做法。
很多团队也是使用这样的结构来进行接口自动化,沿用的话,那在以后的“推广”中,学习和迁移成本低都会比较低。
2.2 测试脚本
首先来简单看一下目前的脚本代码:
![](https://static001.infoq.cn/resource/image/0f/c4/0f93f278621b324caaa9d444bf2377c4.png)
测试脚本结构
有一种做法我一直不提倡,就是把测试用例直接写在 Java 文件中。这样做会带来很多问题:修改测试用例需要改动大量的代码;代码也不便于交接给其他同学,因为每个人都有自己的编码风格和用例设计风格,这样交接,最后都会变成由下一个同学全部推翻重写一遍;如果测试平台更换,无法做用例数据的迁移,只能手动的一条条重新输入。
所以“测试数据”与“脚本”分离是非常有必要的。
网上很多的范例是使用的 Excel 进行的数据驱动,我这里为什么改用 MySQL 而不使用 Excel 了呢?
在公司,我们的脚本和代码都是提交至公司的 Git 代码仓库,如果使用 Excel……很显然不方便日常经常修改测试用例的情况。使用 MySQL 数据库就没有这样的烦恼了,由于数据与脚本的分离,只需对数据进行修改即可,脚本每次会在数据库中读取最新的用例数据进行测试。同时,还可以防止一些操作代码时的误操作。
这里再附上一段我自己写的DataProvider_forDB
方法,方便其他同学使用在自己的脚本上:
2.3 配置文件
上面图中提到了“配置文件”,下面就来简单看一下这个 XML 配置文件的脚本:
![](https://static001.infoq.cn/resource/image/c9/c8/c98e0645b185c5ac39b092edd1406ac8.png)
对照上图来解释一下配置文件:
-
SQL 的话,这里的 SQL 主要决定了选取哪些测试用例进行测试。
-
一个<test>标签,就代表一组测试,可以写多个<test>标签。
-
“listener”是为了最后能够生成一个 ReportNG 的报告。
-
Jenkins 来实现每日构建,可以使用 Maven 插件,通过命令来选择需要执行的 XML 配置。
这样做有什么好处呢?
使用 SQL 最大的好处就是灵活
![](https://static001.infoq.cn/resource/image/3b/0a/3b17adfe2d296edb422469a1a9deb60a.png)
如上面的这个例子,在数据库中会查询出下面这 56 条测试用例,那么这个<test>标签就会对这 56 条用例进行逐一测试。
多<test>
标签时,可以分组展示
![](https://static001.infoq.cn/resource/image/16/91/16e8abb589bfd5c7d4257db2e342ad91.png)
使用多个<test>
标签来区分用例,最大的好处就是也能在最后的报告上,达到一个分组展示的效果。
报告更美观丰富
![](https://static001.infoq.cn/resource/image/37/00/377501ac3f8f1d606ba230eb6e93ff00.png)
由于使用了ReportNG
进行报告的打印,所以报告的展示要比 TestNG 自带的报告要更加美观、并且能自定义展示样式,点开能看到详细的执行过程。
![](https://static001.infoq.cn/resource/image/2a/c2/2adcf61d4f6203569ebd228ae8a70bc2.png)
如果有执行失败的用例,通常报错的用例会在最上方优先展示。
支持多团队
![](https://static001.infoq.cn/resource/image/16/e0/167ed9ec096901b3625a016d6e5d98e0.png)
当两个团队开始使用时,为了方便维护,将基础部分抽出,各个团队的脚本都依赖这个 Base 包,并且将 Base 包版本置为“SNAPSHOT 版本”。使用“SNAPSHOT 版本”的好处是,之后我对 Lego 更新,各个业务组并不需要对脚本做任何改动就能及时更新。
当更多的团队开始使用后,比较直观的看的话是这个样子的:
![](https://static001.infoq.cn/resource/image/f0/35/f0098a578e58ee44a726e38319c4ee35.png)
每个团队的脚本都依赖于我的这个 Base 包,所以最后,各个业务团队的脚本就变成了下面的这个样子:
![](https://static001.infoq.cn/resource/image/ed/df/ed3e9961bc0c2dd349787874567b82df.png)
可以看到,使用了 Lego 之后:
-
没有了 Java 文件,只有 XML 文件
-
xml 中只需要配置 SQL。
-
执行和调试也很方便。
-
可以右键直接执行想要执行的测试配置。
-
可以使用 maven 命令执行测试:
-
mvn clean test -U -Dxml=xmlFileName
。 -
通过参数来选择需要执行的 xml 文件。
-
也可以使用 Jenkins 来实现定时构建测试。
由于,所有测试用例都在数据库所以这段脚本基本不需要改动了,减少了大量的脚本代码量。
有些同学要问,有时候编写一条接口测试用例不只是请求一下接口就行,可能还需要写一些数据库操作啊,一些参数可能还得自己写一些方法才能获取到啊之类的,那不 code 怎么处理呢?
下面就进入“用例设计”,我将介绍我如何通过统一的用例模板来解决这些问题。
三、用例设计
3.1 一些思考
我在做接口自动化设计的时候,会思考通用、校验、健壮、易用这几点。
通用
-
简单、方便
-
用例数据与脚本分离,简单、方便。
-
免去上传脚本的动作,能避免很多不必要的错误和维护时间。
-
便于维护。
-
模板化
-
抽象出通用的模板,可快速拓展。
-
数据结构一致,便于批量操作。
-
专人维护、减少多团队间的重复开发工作。
-
由于使用了统一的模板,那各组之间便可交流、学习、做有效的对比分析。
-
如果以后这个平台不再使用,或者有更好的平台,可快速迁移。
-
可统计、可拓展
-
可统计、可开发工具;如:用例数统计,某服务下有多少条用例等。
-
可开发用例维护工具。
-
可开发批量生成工具。
校验
在写自动化脚本的时候,都会想“细致”,然后“写很多”的检查点;但当“校验点”多的时候,又会因为很多原因造成执行失败。所以我们的设计,需要在保证充足的检查点的情况下,还要尽可能减少误报。
-
充足的检查点
-
可以检查出被测服务更多的缺陷。
-
尽量少的误报
-
可以减少很多的人工检查和维护的时间人力成本。
-
还要
-
简单、易读。
-
最好使用一些公式就能实现自己想要的验证。
-
通用、灵活、多样。
-
甚至可以用在其他项目的检查上,减少学习成本。
健壮
执行测试的过程中,难免会报失败,执行失败可能的原因有很多,简单分为 4 类:
![](https://static001.infoq.cn/resource/image/ec/21/ec2da926199029be5011763d7907c321.png)
-
被测系统出错,这部分其实是我们希望看到的,因为这说明我们的自动化测试真正地发现了一个 Bug,用例发挥了它的价值,所以,这是我们希望看到的。
-
测试工具出错,这部分其实是我们不希望看到的,因为很大可能我们今天的自动化相当于白跑了。
-
测试数据错误,这是我们要避免的,既然数据容易失效,那我在设计测试平台的时候,就需要考虑如果将所有的数据跑“活”,而不是只写“死”。
-
不可抗力,这部分是我们也很无奈的,但是这样的情况很少发生。
那针对上面的情况:
-
参数数据失效
-
支持实时去数据库查询。
-
支持批量查。
-
IP 进场发生变更
-
自动更新 IP。
-
灵活、可复用
-
支持批量维护。
-
接口测试执行前生成一些数据。
-
接口执行完成后销毁一些数据。
-
支持参数使用另一条测试用例的返回结果。
-
支持一些请求参数实时生成,如 token 等数据,从而减少数据失效的问题。
通过这些手段,提高测试用例的健壮性,让每一条自动化测试用例都能很好的完成测试任务,真正发挥出一条测试用例的价值。
易用
-
简单
-
功能强大,但要人人会用。
-
非技术人员也要会用。
-
减少代码操作
-
让自动化开发人员注意力能更多的放在用例本身,而不是浪费在无关紧要的开发工作上面。
-
还要
-
配置能复用。
-
通用、易学。
-
一些数据能自动生成。
3.2 Lego 接口自动化测试用例
说了这么多,那我们来看一下一条 Lego 接口测试用例的样子。
一条 Lego 自动用例执行顺序大概是如下图这样:
![](https://static001.infoq.cn/resource/image/58/41/5892790fa543c931590197e996ec4b41.png)
简单区分一下各个部分,可以看到:
![](https://static001.infoq.cn/resource/image/a4/95/a40358c0e11d600c7f76b397491e9395.png)
那上面图中提到了两个名词:
-
“参数化”
-
“前后置动作”
下面会先对这两个名词做一个简单的介绍。
3.3 参数化
比如一个请求需要用到的参数。
这个例子中有个参数"productId": 00004321
,而由于测试的环境中,表单 00004321 很可能一些状态已经发生了改变,甚至表单已经删除,导致接口请求的失败,那么这时候,就很适合对"productId": 00004321
进行参数化,比如写成这样:
所以对“参数化”简单的理解就是:
通过一些操作,将一个“值”替换掉测试用例里的一个“替代字符”
${myProductId}
的值可以通过配置获取到:
-
Key-Value
-
配置 Value=00004321。
-
SQL 获取
-
执行一个 select 语句来实时查询得到可用 ID。
-
已有测试用例
-
某个接口接口测试用例的返回结果。
“参数化”实例
下面我们来看一个“参数化”的实例:
(1) 首先我们在参数化维护页面中新建一个参数化,shopdealid
。
![](https://static001.infoq.cn/resource/image/d2/ff/d2160fc4787cd6429d8519d66d31f2ff.png)
通过配置我们可以看到这个参数的值,是执行了一条 SQL 后,取用执行结果中DealID
字段的值。
(2) 在用例中,将需要这个表单号的地方用 ${shopdealid}替代。
![](https://static001.infoq.cn/resource/image/b0/f6/b020b3264fb9298579ccc8c0fd3db2f6.png)
那在编写测试用例的时候,大家可以看一下这个放大的图片,在这里的 ProductID 的值并不是硬代码一个固定的表单号,而是选择了刚才配置的参数化数据。
(3) 执行结果中,${shopdealid} 变为实时查询数据库的来的一个真实的表单号。
![](https://static001.infoq.cn/resource/image/3f/76/3f502c306d671332501ba3bac58cf176.png)
从结果中可以看到,我们的这个参数被替换成了一个有效的值,而这个值就是我们刚刚配置的那个 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 得到下表中的结果:
那我们可以使用下面左边的表达式,得到对应的结果:
-
${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) 首先我们在前后置维护页面中新建一个动作,获取库存上限未卖光团单
。
![](https://static001.infoq.cn/resource/image/73/4c/73a642ef4d029e312b08f6b98afa5f4c.png)
这个配置也是可以支持在线调试的,在调试中,可以看到可以使用的参数化:
![](https://static001.infoq.cn/resource/image/b0/86/b0f179c5d02f1a7e21c3c352e780f686.png)
(2) 在测试用例中的前置动作,添加获取库存上限未卖光团单
。
![](https://static001.infoq.cn/resource/image/b6/bb/b616c185e91066533d012ed082d0bbbb.png)
这样就可以在整个测试用例中,使用${pre.ProductID}
,来替换掉原有的数据信息。
(3) 最后请求接口,返回了执行成功 。
![](https://static001.infoq.cn/resource/image/69/33/69fb4713e2867297ba1842adc0b6c433.png)
Q & A
Q:那如果同样是获取三个参数,使用 3 个“参数化的 Select 操作”和使用 1 个“前置动作的 Select 操作”又有什么不同呢?
A: 不同在于执行时间上。
比如,我们查询最新的有效团单的“单号”“下单人”和“手机号”三个字段。
使用 3 个“参数化的 Select 操作”:可能当执行单号的时候得到的订单号是“10001”,但是当执行到{下单人}的时候,可能有谁又下了一单,可能取到的下单人变成了“10002”的“李四”而不是“10001”的“张三”了,最后可能“单号”“下单人”和“手机号”三个字段去的数据并非同一行的数据。
而使用“前置动作的 Select 操作”:就可以避免上面的问题,因为所有字段的数据是一次性查询出来的,就不会出现错位的情况。
Q : 那“参数化的 Select 操作”和“前置动作的 Select 操作”这样不同的取值时机又有什么好用之处呢?
A : 由于“前置动作”一定是接口请求前执行,“参数化”一定是用到的时候才执行这样的特性。
所以在检查点中,如果要验证一个数据库字段在经过接口调用后发生了变更,那使用“前置动作”和“参数化”同时去查询这个字段,然后进行比较,不一致就说明发生了变化。
所以根据使用场景,选择合适的参数化方式,很重要,选择对了,能大大提升测试用例的测试数据健壮性。
3.5 执行各部分
回到一开始的流程图,可以按照一类一类来看执行过程。
测试发起
![](https://static001.infoq.cn/resource/image/05/90/05b7940f4e4ee6198c7143a252e02290.png)
测试发起基本还是使用的 Jenkins,稳定、成熟、简单、公司工具组支持,也支持从 Lego 的 Web 页面进行执行操作。
数据 / 环境准备
![](https://static001.infoq.cn/resource/image/6c/a7/6cf7243338f4a07de319cf31a98c13a7.png)
使用 @DataProvider 的方式,从 DB 数据库中读取测试用例,逐一执行进行测试。
测试执行
![](https://static001.infoq.cn/resource/image/d5/bd/d5640c81e5f5144d1d93b6a49da9d4bd.png)
在正式执行测试用例之前,会先进行一波参数替换的动作,在调用接口之后,还会执行一次参数替换动作。
![](https://static001.infoq.cn/resource/image/d7/ca/d7e9fa6bd1a638ca62de471b4c00b3ca.png)
参数替换后会进行前置动作的执行,然后在调用接口之后还会执行测试后动作,最后执行后置动作。
![](https://static001.infoq.cn/resource/image/6e/a0/6eb9af4097a6df16d42b5225295a31a0.png)
接口请求这部分就没什么好说的了,就是通过接口请求的参数,请求对应的接口,拿到返回结果。
这里的话是为了方便通用,所以要求返回的结果都是使用的 String 类型。这样做最大的好处就是。比如说我现在有一种新的接口类型需要接入。那只需要写一个方法能够请求到这个接口,并且拿到 String 类型的返回结果,就可以很快将新的接口类型接入 Lego 测试平台进行接口测试。
检查点校验
![](https://static001.infoq.cn/resource/image/3b/e2/3be46f8fc11f270935d948b66662d4e2.png)
检查点部分是一条自动化测试用例的精髓,一条自动化测试用例是否能真正的发挥它的测试功能,就是看 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 返回的结果是列表的形式时
除此之外,还有非常多的花样玩法
JsonPath 中的检查支持“参数化”和“前后置动作”,所以会看到很多如:
{.param}=‘{param}’ && {.param}=={pre.param}
这样的检查点:
“参数化”和“前后置动作”也支持递归配置,这些都是为了能够让接口自动化测试用例写的更加灵活好用。
测试结果
![](https://static001.infoq.cn/resource/image/20/77/20c125e27706869ebe7ca6a6698abb77.png)
使用 ReportNG 可以打印出很漂亮的报告。
报告会自定义一些高亮等展示方式,只需要在 ReportNG 使用前加上下面的语句,就可以支持“输出逃逸”,可使用 HTML 标签自定义输出样式。
后期优化
![](https://static001.infoq.cn/resource/image/eb/7d/eb39da530423543d10779c6e7774747d.png)
当使用 Jenkins 执行后,通过 Jenkins API 、和 Base 包中的一些方法,定时获取测试结果,落数据库,提供生成统计图表用。
四、网站功能
4.1 站点开发
既然打算做工具平台了,就得设计方方面面,可惜人手和时间上的不足,只能我一人利用下班时间进行开发。也算是担任了 Lego 平台的产品、后端开发、前端开发、运维和测试等各种角色。
Jenkins+TestNG+ReportNG+我自己开发的基本接口自动化测试 Base jar 包,基本上没什么太大难度。但是站点这块,在来美团之前,还真没开发过这样的工具平台,这个算是我的第一个带 Web 界面的工具。边 Google 边做,没想到不久还真的架起来了一个简易版本。
使用 Servlet + Jsp 进行开发,前端框架使用 Bootstrap,前端数据使用 jstl,数据库使用 MySQL,服务器使用的公司的一台 Beta 环境 Docker 虚拟机,域名是申请的公司内网域名,并开通北京上海两侧内网访问权限。
功能上基本都是要满足的,界面上,虽然做不到惊艳吧,但是绝对不能丑,功能满足,但是长得一副 80 年代的界面,我自己都会嫌弃去使用它,所以界面上我还是花了一些时间去调整和设计。熟练以后就快多了。
4.2 整体组成
![](https://static001.infoq.cn/resource/image/f6/b7/f6183d96e1bdfb82051dffed579e82b7.png)
目前 Lego 由五个不同的项目组成,分别是“测试脚本”、“Lego-web 页面项目”、“用于执行接口测试的 base 包”、“小工具集合 Lego-kit”和“lego-job”,通过上图可以看出各项目间的依赖关系。
细化各个项目的功能,就是下图:
![](https://static001.infoq.cn/resource/image/f0/8c/f0bdf2429fb6ad55904a585c54b9048c.png)
简单来说,网站部分和脚本是分离的,中间的纽带是数据库。所以,没有网站,脚本执行一点问题也没有;同样的,网站的操作,和脚本也没有关系。
4.3 使用-日常维护
Step 1
![](https://static001.infoq.cn/resource/image/8c/18/8c8ed750a72f2a8d076f5dd2e4d73618.png)
每天上班来会收到这样的测试邮件,通过邮件能知道昨晚执行的情况。如果有报错,可以点击“详细报告链接”,跳转到在线报告。
Step 2
![](https://static001.infoq.cn/resource/image/96/0f/96078fac2f794bd2fc0e1b841a061c0f.png)
在现报告可以直接看到执行报错的信息,然后点击“LEGO 维护传送门”,可以跳转到 Lego 站点上,进行用例维护。
Step 3
跳转到站点上以后,可以直接展示出该条测试用例的所有信息。定位,维护、保存,维护用例,可以点击“执行”查看维护后的执行结果,维护好后“保存”即可。
仅仅 3 步,1~2 分钟即可完成对一条执行失败的用例进行定位、调试和维护动作。
4.4 用例编辑
![](https://static001.infoq.cn/resource/image/c4/02/c4c3eeb6dd3dd6e7299d6818538c1602.png)
通过页面,我们就可以对一条测试用例进行:
-
新建
-
复制
-
编辑
-
删除
-
是否放入每日构建中进行测试
4.5 在线调试
![](https://static001.infoq.cn/resource/image/82/e1/82eaa6231de61d138ca33bf35af038e1.png)
lego-web 项目同样的使用 base 进行的用例执行,所以执行结果和打印都与脚本执行的一致的。
4.6 用例生成工具
为了更方便的写用例,针对部分接口开发了一键批量生成用例的小工具。
4.7 执行结果分析
通过 Jenkins 接口、Base 包中基础 Test 方法,将结果收集到数据库,便于各组对测试结果进行分析。
![](https://static001.infoq.cn/resource/image/3a/eb/3afe4d9cf0485ece75a965cb18c816eb.png)
这是每天执行后成功率走势图:
![](https://static001.infoq.cn/resource/image/2d/c4/2ddc1b943ce745b234eb71efd16e1ac4.png)
也可以按月进行统计,生成统计的图表,帮助各个团队进行月报数据收集和统计。
4.8 失败原因跟踪
有了能直观看到测试结果的图表,就会想要跟踪失败原因。
![](https://static001.infoq.cn/resource/image/29/f7/29e1bfaae38838e15db70e80c7cf0ff7.png)
所以在成功率数据的右边,会有这样的跟踪失败原因的入口,也可以很直观地看到哪一些失败的原因还没有被跟踪。点开后可以对失败原因进行记录。
![](https://static001.infoq.cn/resource/image/15/2e/15fd76ee3b2c3a6cfaef8a60972c472e.png)
最后会有生成图表,可以很清晰地看到失败原因以及失败类型的占比。
4.9 代码覆盖率分析
结合 Jacoco,我们可以对接口自动化的代码覆盖率进行分析。
![](https://static001.infoq.cn/resource/image/fe/7d/fe3cc8575096c86975d72d6d41ec077d.png)
在多台 Slave 机器上配置 Jacoco 还是比较复杂的,所以可以开发覆盖率配置辅助工具来帮助测试同学,提高效率。
![](https://static001.infoq.cn/resource/image/aa/c9/aafa35f11b3293548c5ac6f3ca7397c9.png)
4.10 用例优化方向
除了上面的图表,还会给用例优化提供方向。
![](https://static001.infoq.cn/resource/image/35/14/3578101a777337a276dd28912e3b6614.png)
通过用例数量统计的图表,我们可以知道哪些服务用例还比较少,哪些环境的用例还比较少,可以比较有针对性的进行测试用例的补充。
![](https://static001.infoq.cn/resource/image/d4/10/d4618912da617b642036260fab459a10.png)
通过失败原因的图表,我们可以改善自己用例中的“参数化”和“前后置动作”的使用,增加测试用例的健壮性。
![](https://static001.infoq.cn/resource/image/16/1c/16517c3e8744abf34ebf9f2d205c6c1c.png)
通过线上接口调用量排序的图表。我们可以有效的知道优先维护哪些服务的测试用例,通过表格中,我们可以看到,哪些服务已经覆盖了测试用例,哪些没有被覆盖, 给各组的 QA 制定用例开发计划,提供参考。
![](https://static001.infoq.cn/resource/image/09/5d/097cf69c057ec9afebba2e4b086a905d.png)
同时在维护接口自动化测试的时候,都会看到用例评分的情况,来协助 QA 提高用例编写的质量。
4.11 收集反馈/学习
还做了“需求白板”,用来收集使用者的需求和 Bug。除此之外,Lego 平台已经不只是一个接口测试的平台,还可以让想学习开发的 QA 领任务,学习一些开发技巧,提高自己的代码能力。
五、总结
-
为了减少开发成本,使用比较常见的 Jenkins+TestNG 的脚本形式。
-
为了简化 code 操作,使用 DB 进行测试用例存储,并抽象出用例摸版。
-
为了减低新建用例成本,开发“用例维护页面”和“一键生成”等工具。
-
为了减低维护成本,加跳转链接,维护一条用例成本在几分钟内。
-
为了增加用例健壮性,设计了“参数化”、“前后置动作”等灵活的参数替换。
-
为了易用和兼容,统一“返回结果”类型,统一“检查点”的使用。
-
为了接口自动化用例设计提供方向,结合 Jacoco 做代码覆盖率统计,并开发相关配置工具
-
为了便于分析数据,从 DOM、CAT、Jenkins 上爬各种数据,在页面上用图表展示。
-
为了优化用例,提供“用例打分”、“线上调用量排行”等数据进行辅助。
![](https://static001.infoq.cn/resource/image/0a/94/0a439b48664efca93d6eb7cd174e7694.png)