在測試用例中,包含預期結果這麼一項,用於輔助測試人員執行測試用例時判斷系統的功能是否正常。而在自動化測試中,咱們的目標是讓測試用例自動執行,所以自動化測試用例中一樣須要包含預期結果一項,只不過系統響應結果再也不由人工來進行判斷,而是交由測試工具或框架來實現。html
這部分功能對應的就是測試結果校驗器(validator),基本上能稱得上自動化測試工具或框架的都包含該功能特性。python
HttpRunner
在設計之初,結果校驗器(validator)的實現比較簡單。git
對於每個test
,能夠指定0個或多個校驗項,放置在validate
中。在自動化測試執行的時候,會在發起HTTP請求、解析結果響應以後,逐個檢查各個校驗項,若存在任意校驗項不經過的狀況,則該test
將終止並被標記爲失敗。github
- test:
name: get token
request:
url: http://127.0.0.1:5000/api/get-token
method: GET
extract:
- token: content.token
validate:
- {"check": "status_code", "comparator": "eq", "expect": 200}
- {"check": "content.token", "comparator": "len_eq", "expect": 16}
複製代碼
如上例所示,每個校驗項均爲一個json
結構,裏面包含check
、expect
、comparator
三個屬性字段。其中,check
對應着要檢查的字段,expect
對應着檢查字段預期的值,這兩項是必須指定的;comparator
字段對應着比較方法,若不指定,則默認採用eq
,即檢查字段與預期值相等。正則表達式
爲了實現儘量強大的檢查功能,check
屬性值可經過鏈式操做精確指定具體的字段,comparator
也內置實現了大量的檢查功能。算法
舉個例子可能會更清晰些。假如某結構的響應結果以下:數據庫
// status code: 200
// response headers
{
"Content-Type": "application/json"
}
// response body content
{
"success": False,
"person": {
"name": {
"first_name": "Leo",
"last_name": "Lee",
},
"age": 29,
"cities": ["Guangzhou", "Shenzhen"]
}
}
複製代碼
那麼假如咱們要檢查status code
,check
就能夠指定爲status_code
;假如要檢查response headers
中的Content-Type
,check
就能夠指定爲headers.content-type
;假如要檢查response body
中的first_name
,check
就能夠指定爲content.person.name.first_name
。能夠看出,假以下一層級爲字典結構,那麼就能夠經過.
運算符指定下一層級的key
,依次類推。json
對於字段內容爲列表list
的狀況略有不一樣,咱們須要經過序號來指定具體檢查哪一項內容。例如,Guangzhou
對應的檢查項爲content.person.cities.0
,Shenzhen
對應的檢查項爲content.person.cities.1
。api
在比較方式(comparator
)方面,HttpRunner
除了eq
,還內置了大量的檢查方法。例如,咱們能夠經過gt
、ge
、lt
、le
等比較數值大小,經過len_eq
、len_gt
、len_lt
等比較長度是否相等(列表、字典、字符串均適用),經過contains
、contained_by
來判斷包含關係,經過startswith
、endswith
判斷字符串的開頭結尾,甚至經過regex_match
來判斷是否知足正則匹配等。詳細的比較方式還有許多,須要時可查看comparator表格。app
在大多數狀況下,HttpRunner
的結果校驗器(validator)是夠用的。不過問題在於,框架不可能爲用戶實現全部的檢查方法,假如用戶須要某些特殊的檢查方法時,HttpRunner
就無法實現了。
這的確是一個問題,以前Junho2010
提的issue #29中舉了一個例子,應該也算是比較有表明性。
發送請求時的數據使用了隨機生成,而後須要比較結果中的數據是不是和這個相關(經過某個算法轉換)。好比我輸入的是321,個人結果是
(3+2+1) * avg(3+2+1)
這種轉化,目前的comparator是比較難於實現的。
要解決這個問題,最好的方式應該是在HttpRunner
中實現自定義結果校驗器的機制;用戶在有須要的時候,能夠本身編寫校驗函數,而後在validate
中引用校驗函數。以前也介紹過HttpRunner
的熱加載機制,《約定大於配置:ApiTestEngine實現熱加載機制》,自定義結果校驗器應該也是能夠採用這種方式來實現的。
第二個須要優化的點,HttpRunner
的結果校驗器還不支持變量引用,會形成某些場景下的侷限性。例如,testwangchao
曾提過一個issue #52:
接口response內,會返回數據庫內的自增ID。ID校驗的時候,但願
expected
爲參數化的值。
validate:
- {"check": "content.data.table_list.0.id", "expected": "$id"}
複製代碼
另外,在《ApiTestEngine,再也不侷限於API的測試》一文中有介紹過,結果提取器(extract
)新增實現了經過正則表達式對任意文本響應內容的字段提取。考慮到結果校驗器(validate
)也須要先從結果響應中提取出特定字段才能與預期值進行比較,在具體實現上徹底能夠複用同一部分代碼,所以在validate
的check
部分也能夠進行統一化處理。
通過前面的侷限性問題描述,咱們的改造目標也明確了,主要有三個方面:
具體的改造過程就不寫了,有興趣的同窗能夠直接閱讀源碼,重點查看httprunner/context.py
中的parse_validator
、do_validation
和validate
三個函數。
通過優化後,改造目標中的三項功能都實現了。爲了更好地展示改造後的結果校驗器,此處將結合實例進行演示。
先來看第一個優化項,新增支持自定義結果校驗器。
假設咱們須要使用HTTP響應狀態碼各個數字的和來進行校驗,例如,201
狀態碼對應的數字和爲3,503
狀態碼對應的數字和爲8。該實例只是爲了演示用,實際上並不會用到這樣的校驗方式。
首先,該種校驗方式在HttpRunner
中並無內置,所以須要咱們本身來實現。實現方式與熱加載機制相同,只須要將自定義的校驗函數放置到當前YAML/JSON
文件同級或者父級目錄的debugtalk.py
中。
對於自定義的校驗函數,須要遵循三個規則:
debugtalk.py
中assert
將實際運算結果與預期結果值進行比較對於前面提到的演示案例,咱們就能夠在debugtalk.py
中編寫以下校驗函數。
def sum_status_code(status_code, expect_sum):
""" sum status code digits e.g. 400 => 4, 201 => 3 """
sum_value = 0
for digit in str(status_code):
sum_value += int(digit)
assert sum_value == expect_sum
複製代碼
而後,在YAML/JSON
格式測試用例的validate
中,咱們就能夠將校驗函數名稱sum_status_code
做爲comparator
進行使用了。
- test:
name: get token
request:
url: http://127.0.0.1:5000/api/get-token
method: GET
validate:
- {"check": "status_code", "comparator": "eq", "expect": 200}
- {"check": "status_code", "comparator": "sum_status_code", "expect": 2}
複製代碼
因而可知,自定義的校驗函數sum_status_code
與HttpRunner
內置的校驗方法eq
在使用方式上徹底相同,應該沒有理解上的難度。
對於第二個優化項,結果校驗器中實現變量引用。在使用方式上咱們應該與request
中的變量引用一致,即經過$var
的方式來引用變量var
。
- test:
name: get token
request:
url: http://127.0.0.1:5000/api/get-token
method: GET
variables:
- expect_status_code: 200
- token_len: 16
extract:
- token: content.token
validate:
- {"check": "status_code", "comparator": "eq", "expect": "$expect_status_code"}
- {"check": "content.token", "comparator": "len_eq", "expect": "$token_len"}
- {"check": "$token", "comparator": "len_eq", "expect": "$token_len"}
複製代碼
經過以上示例能夠看出,在結果校驗器validate
中,check
和expect
都可實現實現變量的引用;而引用的變量,能夠來自四種類型:
test
中定義的variables
,例如expect_status_code
test
中提取(extract
)的結果變量,例如token
testset
中,先前test
中提取(extract
)的結果變量testset
中,全局配置config
中定義的變量而check
字段除了能夠引用變量,以及保留了以前的鏈式操做定位字段(例如上例中的content.token
)外,還新增了採用正則表達式提取內容的方式,也就是第三個優化項。
假設以下接口的響應結果內容爲LB123abcRB789
,那麼要提取出abc
部分進行校驗,就能夠採用以下描述方式。
- test:
name: get token
request:
url: http://127.0.0.1:5000/api/get-token
method: GET
validate:
- {"check": "LB123(.*)RB789", "comparator": "eq", "expect": "abc"}
複製代碼
可見在使用方式上與在結果提取器(extract
)中徹底相同。
最後,爲了進一步簡化結果校驗的描述,我在validate
中新增實現了一種描述方式。
簡化後的描述方式與原始方式對好比下:
validate:
- comparator_name: [check_item, expect_value]
- {"check": check_item, "comparator": comparator_name, "expect": expect_value}
複製代碼
一樣是前面的例子,採用新的描述方式後會更加簡潔。而兩種方式表達的含義是徹底等價的。
- test:
name: get token
request:
url: http://127.0.0.1:5000/api/get-token
method: GET
validate:
- eq: ["status_code", $expect_status_code]
- sum_status_code: ["status_code", 2]
- len_eq: ["$token", $token_len]
- len_eq: ["content.token", 16]
- eq: ["LB123(.*)RB789", "abc"]
複製代碼
固然,這次優化保證了與歷史版本的兼容,以前編寫的測試用例腳本的運行是徹底不會受到任何影響的。