从编写《接口自动化测试的最佳工程实践(ApiTestEngine)》至今,已经快半年了。在这一段时间内,ApiTestEngine
通过持续迭代,也已彻底实现了当初预设的目标。html
然而,在设计ApiTestEngine
之初只考虑了面向最常规的API接口类型,即HTTP
响应内容为JSON
数据结构的类型。那么,若是HTTP
接口响应内容不是JSON
,而是XML
或SOAP
,甚至为HTML
呢?web
答案是,不支持!正则表达式
不支持的缘由是什么呢?json
其实,不论是何种业务类型或者技术架构的系统接口,咱们在对其进行测试时均可以拆分为三步:api
而ApiTestEngine
不支持XML/HTML
类型的接口,问题偏偏是出如今解析接口响应
和校验测试结果
这两个环节。考虑到校验测试结果
环节是依赖于解析接口响应
,即须要先从接口响应结果中解析出具体的字段,才能实现与预期结果的校验检测,所以,制约ApiTestEngine
没法支持XML/HTML
类型接口的根本缘由在于没法支持对XML/HTML
的解析。bash
也由于这个缘由,ApiTestEngine
存在局限性,无法推广到公司内部的全部项目组。遇到JSON
类型之外的接口时,只能再使用别的测试工具,体验上非常不爽。数据结构
在经历了一段时间的不爽后,我开始从新思考ApiTestEngine
的设计,但愿使其具备更大的适用范围。经过前面的分析咱们也不难看出,解决问题的关键在于实现针对XML/HTML
的解析器。架构
在实现XML/HTML
的解析器以前,咱们不妨先看下ApiTestEngine
的JSON
解析器是怎么工做的。app
在JSON
类型的数据结构中,不管结构有多么复杂,数据字段都只可能为以下三种数据类型之一:函数
基于这一背景,ApiTestEngine
在实现JSON
的字段提取器(extractor
)时,就采用了点(.
)的运算符。
例如,假如HTTP
接口响应的headers
和body
为以下内容:
response headers:
{
"Content-Type": "application/json",
"Content-Length": 69
}复制代码
response body:
{
"success": false,
"person": {
"name": {
"first_name": "Leo",
"last_name": "Lee",
},
"age": 29,
"cities": ["Guangzhou", "Shenzhen"]
}
}复制代码
那么对应的字段提取方式就为:
"headers.content-type" => "application/json"
"headers.content-length" => 69
"body.success"/"content.success"/"text.success" => false
"content.person.name.first_name" => "Leo"
"content.person.age" => 29
"content.person.cities" => ["Guangzhou", "Shenzhen"]
"content.person.cities.0" => "Guangzhou"
"content.person.cities.1" => "Shenzhen"复制代码
能够看出,经过点(.
)运算符,咱们能够从上往下逐级定位到具体的字段:
.key
来指定下一级的节点,例如.person
,指定了content
下的person
节点;.index
来指定下一级的节点,例如.0
,指定了cities
下的第一个元素。定位到具体字段后,咱们也就能够方便地提取字段值供后续使用了,做为参数或者进行结果校验都可。
从点(.
)运算符的描述形式上来看,它和XML/HTML
的xpath
十分相似。既然如此,那咱们针对XML/HTML
类型的接口,是否能够基于xpath
来实现解析器呢?
在大多数状况下的确能够。例如,针对以下HTML页面,当咱们要获取标题信息时,咱们就能够经过xpath
来指定提取字段:body/h1
<html>
<body>
<h1>订单页面</h1>
<div>
<p>订单号:SA89193</p>
</div>
</body>
</html>复制代码
然而,若是咱们想获取订单号(SA89193)时,使用xpath
就没有办法了(经过body/div/p
获取到的是订单号:SA89193
,还需进一步地进行处理)。
那除了xpath
,咱们还能使用什么其它方法从XML/HTML
中提取特定字段呢?
因为早些年对LoadRunner
比较熟悉,所以我首先想到了LoadRunner
的web_reg_save_param
函数;在该函数中,咱们能够经过指定左右边界(LB & RB)来查找字段,将其提取出来并保存到变量中供后续使用。借鉴这种方式虽然可行,但在描述方式上仍是比较复杂,特别是在YAML
测试用例的extract
中描述的时候。
再一想,这种方式的底层实现不就是正则表达式么。并且咱们经过Python脚本解析网页时,采用正则表达式来对目标字段进行匹配和提取,的确也是通用性很是强的方式。
例如,假设咱们如今想从http://debugtalk.com
首页中提取出座右铭,经过查看网页源代码,咱们能够看到座右铭对应的位置。
<h2 class="blog-motto">探索一个软件工程师的无限可能</h2>复制代码
那么,要提取“探索一个软件工程师的无限可能”字符串时,咱们就可使用正则表达式r"blog-motto\">(.*)</h2>"
进行匹配,而后使用regex
的group
将匹配内容提取出来。
对应的Python脚本实现以下所示。
>>> import re, requests
>>> resp = requests.get("http://debugtalk.com")
>>> content = resp.text
>>> matched = re.search(r"blog-motto\">(.*)</h2>", content)
>>> matched.group(1)
'探索一个软件工程师的无限可能'复制代码
思路肯定后,实现起来就很快了。
此处省略256字。。。
最终,我在ApiTestEngine
中新增实现了一个基于正则表达式的提取器。使用形式与JSON解析保持一致,只须要将以前的点(.
)运算符更改成正则表达式便可。
仍是前面提取座右铭的例子,咱们就能够经过YAML
格式来编写测试用例。
- test:
name: demo
request:
url: http://debugtalk.com/
method: GET
extract:
- motto: 'blog-motto\">(.*)</h2>'
validate:
- {"check": "status_code", "expected": 200}复制代码
须要说明的是,指定的正则表达式必须知足r".*\(.*\).*"
的格式要求,必须而且只能有一个分组(即一对括号)。若是在同一段内容中须要提取多个字段,那就分屡次匹配便可。
实现了基于正则表达式的提取器后,咱们就完全实现了对任意格式HTTP
响应内容的解析,不只限于XML/HTML
类型,对于任意基于HTTP
协议的的接口,ApiTestEngine
均可以适用了。固然,若是接口响应是JSON
类型,咱们虽然能够也使用正则表达式提取,但更建议采用原有的点(.
)运算符形式,由于描述更清晰。
至此,ApiTestEngine
能够说是真正意义上实现了,面向任意类型的HTTP
协议接口,只须要编写维护一份YAML
用例,便可同时实现接口自动化测试、性能测试、持续集成、线上监控的全测试类型覆盖!
如今看来,ApiTestEngine
的名字与其实际功能有些不大匹配了,是该考虑更名了。