优秀的接口测试工具这么多,我到底该选择哪一种?
一、选型
eoLinker纯图形界面、零代码量使用方便,支持在浏览器中安装插件,且提供了测试用例的管理,可以自动化执行所有用例。但网页版所有数据都是保存在服务商的数据库中,若公司涉及到保密问题,则不可以使用网页版,且纯图形界面不支持上传附件这类接口的测试。虽然开源版支持本地部署,但是功能非常的少。全功能本地部署,费用昂贵。
Request+Unittest是测试框架,纯代码级,python语言。应对各种需求,开发起来都很灵活。但是相对于图形化的工具而已,对人员水平要求较高,上手速度明显慢于图形化工具。若公司中有专门的接口自动化测试开发团队,这将是一个很好的选择。既然是语言开发类,也可以使用java+testng,可根据团队的语言能力,选择开发框架。
Postman大部分web项目的开发人员和测试人员都会选择使用这个工具。图形化的界面,操作方便,执行结果易阅读,易调试。可以实现自动化批量运行。但免费版仅支持http和https协议,且无法读写数据库。
Jmeter支持很多种类的协议,支持从数据库动态取值,支持第三方插件,支持接口对文件的操作。由于代码是开源的,即便没有支持的协议,也可以进行二次开发来完成。但测试报告偏向于性能,大部分是性能指标,且断言功能不够强大,虽然提供了beanshell断言,但是提高了使用门槛,需要一点的编程能力。
为了节省成本,我选择开源软件。为了降低开发时间,我选择图形化的工具,减少脚本的编写。我自身具备一定的开发能力,且需要做性能测试,所以我选择Jmeter。在开发完性能测试脚本后,只要稍加修改,就可以获得接口自动化测试脚本。大家还是需要根据自身情况合理选择工具。
选择好工具后,编写测试脚本。我们需要创建线程组、配置元件以及一系列的请求。这些都是入门的内容,百度上都有,这里就不说了。下面给大家分享一下每个项目都用的着的一些技巧:①如何提取响应结果中的某个参数?②获得参数后,如何使用?③如何让系统自动判断响应结果是否正确?
二、提取参数
在实际项目中,有很多场景需要使用到响应结果中的参数。例如登录的响应结果中包含了token,后续的请求,都要使用这个token作为身份认证。例如QQ邮箱中修改一封草稿邮件,在修改之前,我们要获取这封邮件的sid。例如需要确认某个请求响应时间是否在5秒以内,我们需要获取发送时间和响应时间。
目前很多项目的接口返回参数,都是以json格式去发送的。所以我们可以用json提取器来获取想要的参数。若不是json格式,我们可以使用万能的正则表达式提取器来提取。除了这2种常用的方法外,jmeter还提供了近10种方法。
第一种方法,json提取。选中登录请求,选择后置处理器→json提取器。
Names of created variables:变量名
JSON Path expressions:需要提取内容的路径
Match No.(0 for Random):提取第几个匹配的值(若有N个匹配的值,0表示随机取值,-1表示全部取值,1表示取第1个,2表示取第2个。。。N表示取第N个)
Compute concatenation var (suffix_All):勾选表示取所有的值,并保存到一个变量中,命名方式是第一行设置的变量名加上_All
Default Values:没有提取到值,将给变量一个默认值
下面举例说明
1.{
2. "errno": 0,
3. "data": {
4. "user_name": "zhangsan",
5. "mobile": "19900000001",
6. "created_at": "2019-09-06 15:55:21",
7. "real_name": "张三",
8. "group_info": {
9. "user_info": {
10. "mobile": "17700000001",
11. "created_at": "2019-09-06 15:55:21",
12. "token_type": 2,
13. "user_role": "普通用户",
14. "login_count": 188,
15. "password": "123456",
16. "updated_at": "2019-09-06 15:55:21",
17. "id": "991222",
18. },
19. {
20. "mobile": "17700000002",
21. "created_at": "2019-09-21 11:33:16",
22. "token_type": 2,
23. "user_role": "普通用户",
24. "login_count": 157,
25. "password": "123456",
26. "updated_at": "2019-09-22 09:50:01",
27. "id": "991223",
28. },
29. },
30. "token_type": 2,
31. "access_token": "d791640014b411eabf110bd02de67c73",
32. "login_count": 871,
33. "user_id": "331236",
34. "finger_sign": "147"
35. },
36. "errmsg": ""
37.}
这是一个登录后的响应请求,若我们需要提取access_token,则可以这样设置
第一行设定变量名为token,第二行根据给定的路径进行查找,找到后将值赋给token。若找不到,将最后一行的“nodata”赋给token。根据$.data.access_token,我们可以找到第31行的d791640014b411eabf110bd02de67c73并将他赋给token,以后的请求,我们都可以使用这个变量token了。
Json提取器很简单,能否准确提取到想要的内容,主要就是JSON Path expressions设置的正确与否。在第一篇selenium自动化测试中,我们说过Xpath,其实json path与之类似,具体语法对比如下
1.{ "store": {
2. "book": [
3. { "category": "reference",
4. "author": "Nigel Rees",
5. "title": "Sayings of the Century",
6. "price": 8.95
7. },
8. { "category": "fiction",
9. "author": "Evelyn Waugh",
10. "title": "Sword of Honour",
11. "price": 12.99
12. },
13. { "category": "fiction",
14. "author": "Herman Melville",
15. "title": "Moby Dick",
16. "isbn": "0-553-21311-3",
17. "price": 8.99
18. },
19. { "category": "fiction",
20. "author": "J. R. R. Tolkien",
21. "title": "The Lord of the Rings",
22. "isbn": "0-395-19395-8",
23. "price": 22.99
24. }
25. ],
26. "bicycle": {
27. "color": "red",
28. "price": 19.95
29. }
30. }
31.}
示例表达式:
$.store.book[*].author:商店所有书籍的作者(四个作者)
$..author :所有作者
$.store.* :商店所有的东西,包括book和bicycle
$.store..price :所有东西的价格
$..book[2] :第三本书
$..book[0,1] /$..book[:2] :前两本书
?$..book[?(@.isbn)] :用isbn编号过滤所有书籍
$..book[?(@.price<10)] :过滤所有比10更便宜的书
$..* :XML文档中的所有元素
第二种方法,正则表达式提取。
和json类似,关键就在表达式"access_token":"(.*?)"
在响应的数据中,其中有一行是这样的:
那么我们的表达式,括号中的内容,就代表要提取的内容;
.号代表匹配任意字符串;
+号代表匹配一次或多次;
*号代表匹配任意次(包括0次);
?号代表不要贪婪,找到第一个匹配项,就停止匹配;
正则表达式的语法比较复杂,这里就不过多的介绍了,有兴趣可以自己搜集资料,去练习。
三、参数传递
参数传递分为两类,线程内传递、线程间传递。以上面提取到的token为例,若想使用这个参数,在线程内是这样写的:${token}
跨线程传递,需要借助beanshell取样器,先将变量通过setProperty函数设置为全局变量,然后其他线程再通过P函数去引用,具体做法如下:
登录和设置全局变量 这2个请求在同一个线程组内,所以登录请求获取到的token,在设置全局变量的请求中,可以直接使用${token},前面的“allThreadUseToken”是全局变量名,可以随便起,自己记得住就行,最好做到见名知意。
“第一次登录需要设置手势密码”这个请求和登录请求在不同的线程组,所以无法直接使用${token},必须使用allThreadUseToken,格式为${__P(allThreadUseToken)},如下图:
四、断言
所有请求都可以运行起来,我们还需要检查他的响应结果是否正确。Jmeter提供了很多断言方式,和上述的json提取器的样式类似,设置都很简单。下面对beanshell断言进行一个介绍,因为其比较灵活,可以自己写断言内容。
1.if("${realTime}".equals("0")){
2. Failure = true;
3. FailureMessage = "没有获取到时间信息";
4.}else if("${realCode}".equals("{__P(sendCode)}")){
5.long t = Long.valueOf("${realTime}")-Long.valueOf("${__P(sendTime)}");
6.if(t < 0){
7. t = 0 - t;
8.}
9.if(t <= 5){
10. Failure = false;
11. FailureMessage = "服务器的响应时间为" + t + "S" +
12. "----响应时间戳:${realTime};发送时间戳:${__P(sendTime)}";
13. }else{
14. Failure = true;
15. FailureMessage = "服务器的响应时间为" + t + "S" +
16. "----响应时间戳:${realTime};发送时间戳:${__P(sendTime)}";
17. }
18.}else{
19. Failure = true;
20. FailureMessage = "有响应,但编码错乱";
21. }
第一行的${realTime}是响应结果中获取到的实际时间,第四行的${realCode}是获取到的时间格式,第四行的{__P(sendCode)}是另外一个线程内自定义的时间格式,第五行的${__P(sendTime)}是另外一个线程内自定义的发送时间。
Beanshell断言中Failure取true,代表发现错误,预期结果与实际结果不符;Failure取false,代表预期结果和实际结果相同;FailureMessage是输出的错误信息。
上述代码的意思为:先判断是否提取到了真实响应时间,若没提取到,代表请求失败了。若提取到,则判断时间格式是否正确。若格式正确则比较发送时间和响应时间是否大于5秒。
还是以登录为例,若数据库中正确的帐号密码是“zhangsan”,“123456”。发送登录请求,输入正确的帐号密码,请求成功,响应结果如下
此时我们可以提取errno为0进行beanshell断言。
若发送请求时,给出了错误的帐号密码,响应结果如下
此时我们可以根据errno为2000进行beanshell断言,并同时判断提示信息errmsg内容是否正确。断言方式需要灵活选择,尽量直接使用jmeter提供的断言方法,减少开发成本,最后选择beanshell断言。接口测试最重要的部分就是断言,一个没有断言的自动化接口测试脚本,其测试结果毫无意义。
五、集成到jenkins
Jmeter图形化页面进行脚本编写,调试,相当方便。为了提高运行速度,建议使用命令行模式运行脚本。
Jenkins中,创建一个自由风格的软件项目。
若是需要远程节点运行,在general中勾选“限制项目的运行节点”,如何添加远程节点,已经在本系列的第一篇中详细介绍过了。我这里的jmeter的脚本,都是基于图形化界面开发的,所以不需要进行源码管理。定时每周1到周6,中午12点30分运行一次。
当前运行环境是windows环境,所以用的是批处理命令。若是linux就使用shell命令。Jmeter的命令行模式运行的一些命令,可以在官方网站上查看介绍,具体网站是:https://jmeter.apache.org英语不好的,可以使用google浏览器,直接可以将英文网站翻译成中文网站。
放开样式权限(系列二中已经介绍过)
Groovy Script:System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
设置在线查阅测试报告(系列二中已经介绍过)
Publish HTML reports
将测试结果发送到指定邮箱(系列二中已经介绍过)
构建成功可以在点击html report在线查看运行结果
点击阅读☞
点击阅读☞
点击阅读☞
点击阅读☞
点击阅读☞