從編寫《接口自動化測試的最佳工程實踐(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
的名字與其實際功能有些不大匹配了,是該考慮更名了。