Lego-美团接口自动化测试实践

发表于:2018-12-06 12:04

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:永达    来源:美团技术团队

分享:
   被测系统出错,这部分其实是我们希望看到的,因为这说明我们的自动化测试真正地发现了一个Bug,用例发挥了它的价值,所以,这是我们希望看到的。
  测试工具出错,这部分其实是我们不希望看到的,因为很大可能我们今天的自动化相当于白跑了。
  测试数据错误,这是我们要避免的,既然数据容易失效,那我在设计测试平台的时候,就需要考虑如果将所有的数据跑“活”,而不是只写“死”。
  不可抗力,这部分是我们也很无奈的,但是这样的情况很少发生。
  那针对上面的情况:
  参数数据失效
  支持实时去数据库查询。
  支持批量查。
  IP进场发生变更
  自动更新IP。
  灵活、可复用
  支持批量维护。
  接口测试执行前生成一些数据。
  接口执行完成后销毁一些数据。
  支持参数使用另一条测试用例的返回结果。
  支持一些请求参数实时生成,如token等数据,从而减少数据失效的问题。
  通过这些手段,提高测试用例的健壮性,让每一条自动化测试用例都能很好的完成测试任务,真正发挥出一条测试用例的价值。
  易用
  简单
  功能强大,但要人人会用。
  非技术人员也要会用。
  减少代码操作
  让自动化开发人员注意力能更多的放在用例本身,而不是浪费在无关紧要的开发工作上面。
  还要
  配置能复用。
  通用、易学。
  一些数据能自动生成。
  3.2 Lego接口自动化测试用例
  说了这么多,那我们来看一下一条Lego接口测试用例的样子。
  一条Lego自动用例执行顺序大概是如下图这样:
  
  简单区分一下各个部分,可以看到:
  
  那上面图中提到了两个名词:
  “参数化”
  “前后置动作”
  下面会先对这两个名词做一个简单的介绍。
  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。
 
  通过配置我们可以看到这个参数的值,是执行了一条SQL后,取用执行结果中DealID字段的值。
  (2) 在用例中,将需要这个表单号的地方用${shopdealid}替代。
  
  那在编写测试用例的时候,大家可以看一下这个放大的图片,在这里的ProductID的值并不是硬代码一个固定的表单号,而是选择了刚才配置的参数化数据。
  (3) 执行结果中,${shopdealid} 变为实时查询数据库的来的一个真实的表单号。
 
  从结果中可以看到,我们的这个参数被替换成了一个有效的值,而这个值就是我们刚刚配置的那个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) 首先我们在前后置维护页面中新建一个动作,获取库存上限未卖光团单 。
  
  这个配置也是可以支持在线调试的,在调试中,可以看到可以使用的参数化:
 
  (2) 在测试用例中的前置动作,添加获取库存上限未卖光团单 。
  
  这样就可以在整个测试用例中,使用${pre.ProductID},来替换掉原有的数据信息。
  (3) 最后请求接口,返回了执行成功 。
  
  Q & A
  Q:那如果同样是获取三个参数,使用3个“参数化的Select操作”和使用1个“前置动作的Select操作”又有什么不同呢?
  A: 不同在于执行时间上。
  比如,我们查询最新的有效团单的“单号”“下单人”和“手机号”三个字段。
  使用3个“参数化的Select操作”:可能当执行${单号}的时候得到的订单号是“10001”,但是当执行到${下单人}的时候,可能有谁又下了一单,可能取到的下单人变成了“10002”的“李四”而不是“10001”的“张三”了,最后可能“单号”“下单人”和“手机号”三个字段去的数据并非同一行的数据。
  而使用“前置动作的Select操作”:就可以避免上面的问题,因为所有字段的数据是一次性查询出来的,就不会出现错位的情况。
  Q : 那“参数化的Select操作”和“前置动作的Select操作”这样不同的取值时机又有什么好用之处呢?
  A : 由于“前置动作”一定是接口请求前执行,“参数化”一定是用到的时候才执行这样的特性。
  所以在检查点中,如果要验证一个数据库字段在经过接口调用后发生了变更,那使用“前置动作”和“参数化”同时去查询这个字段,然后进行比较,不一致就说明发生了变化。
  所以根据使用场景,选择合适的参数化方式,很重要,选择对了,能大大提升测试用例的测试数据健壮性。
  3.5 执行各部分
  回到一开始的流程图,可以按照一类一类来看执行过程。
  测试发起
  
  测试发起基本还是使用的Jenkins,稳定、成熟、简单、公司工具组支持,也支持从Lego的Web页面进行执行操作。
  数据 / 环境准备
  
  使用 @DataProvider 的方式,从DB数据库中读取测试用例,逐一执行进行测试。
  测试执行
  
  在正式执行测试用例之前,会先进行一波参数替换的动作,在调用接口之后,还会执行一次参数替换动作。
  
  参数替换后会进行前置动作的执行,然后在调用接口之后还会执行测试后动作,最后执行后置动作。
  
  接口请求这部分就没什么好说的了,就是通过接口请求的参数,请求对应的接口,拿到返回结果。
  这里的话是为了方便通用,所以要求返回的结果都是使用的String类型。这样做最大的好处就是。比如说我现在有一种新的接口类型需要接入。那只需要写一个方法能够请求到这个接口,并且拿到String类型的返回结果,就可以很快将新的接口类型接入Lego测试平台进行接口测试。
  检查点校验
  
  检查点部分是一条自动化测试用例的精髓,一条自动化测试用例是否能真正的发挥它的测试功能,就是看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}
  这样的检查点:
  “参数化”和“前后置动作”也支持递归配置,这些都是为了能够让接口自动化测试用例写的更加灵活好用。
  测试结果
   用ReportNG可以打印出很漂亮的报告。
  报告会自定义一些高亮等展示方式,只需要在ReportNG使用前加上下面的语句,就可以支持“输出逃逸”,可使用HTML标签自定义输出样式。
  System.setProperty("org.uncommons.reportng.escape-output", "false");
  后期优化
  
  当使用Jenkins执行后,通过Jenkins API 、和Base包中的一些方法,定时获取测试结果,落数据库,提供生成统计图表用。
  四、网站功能
  4.1 站点开发
  既然打算做工具平台了,就得设计方方面面,可惜人手和时间上的不足,只能我一人利用下班时间进行开发。也算是担任了Lego平台的产品、后端开发、前端开发、运维和测试等各种角色。
  Jenkins+TestNG+ReportNG+我自己开发的基本接口自动化测试Base jar包,基本上没什么太大难度。但是站点这块,在来美团之前,还真没开发过这样的工具平台,这个算是我的第一个带Web界面的工具。边Google边做,没想到不久还真的架起来了一个简易版本。
  使用 Servlet + Jsp 进行开发,前端框架使用Bootstrap,前端数据使用jstl,数据库使用MySQL,服务器使用的公司的一台Beta环境Docker虚拟机,域名是申请的公司内网域名,并开通北京上海两侧内网访问权限。
  功能上基本都是要满足的,界面上,虽然做不到惊艳吧,但是绝对不能丑,功能满足,但是长得一副80年代的界面,我自己都会嫌弃去使用它,所以界面上我还是花了一些时间去调整和设计。熟练以后就快多了。
  4.2 整体组成
 
  目前Lego由五个不同的项目组成,分别是“测试脚本”、“Lego-web页面项目”、“用于执行接口测试的base包”、“小工具集合Lego-kit”和“lego-job”,通过上图可以看出各项目间的依赖关系。
  细化各个项目的功能,就是下图:
  
  简单来说,网站部分和脚本是分离的,中间的纽带是数据库。所以,没有网站,脚本执行一点问题也没有;同样的,网站的操作,和脚本也没有关系。

      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
 
32/3<123>
精选软件测试好文,快来阅读吧~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号