HttpRunner 的結果校驗器優化

在測試用例中,包含預期結果這麼一項,用於輔助測試人員執行測試用例時判斷系統的功能是否正常。而在自動化測試中,咱們的目標是讓測試用例自動執行,所以自動化測試用例中一樣須要包含預期結果一項,只不過系統響應結果再也不由人工來進行判斷,而是交由測試工具或框架來實現。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結構,裏面包含checkexpectcomparator三個屬性字段。其中,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 codecheck就能夠指定爲status_code;假如要檢查response headers中的Content-Typecheck就能夠指定爲headers.content-type;假如要檢查response body中的first_namecheck就能夠指定爲content.person.name.first_name。能夠看出,假以下一層級爲字典結構,那麼就能夠經過.運算符指定下一層級的key,依次類推。json

對於字段內容爲列表list的狀況略有不一樣,咱們須要經過序號來指定具體檢查哪一項內容。例如,Guangzhou對應的檢查項爲content.person.cities.0Shenzhen對應的檢查項爲content.person.cities.1api

在比較方式(comparator)方面,HttpRunner除了eq,還內置了大量的檢查方法。例如,咱們能夠經過gtgeltle等比較數值大小,經過len_eqlen_gtlen_lt等比較長度是否相等(列表、字典、字符串均適用),經過containscontained_by來判斷包含關係,經過startswithendswith判斷字符串的開頭結尾,甚至經過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)也須要先從結果響應中提取出特定字段才能與預期值進行比較,在具體實現上徹底能夠複用同一部分代碼,所以在validatecheck部分也能夠進行統一化處理。

通過前面的侷限性問題描述,咱們的改造目標也明確了,主要有三個方面:

  • 新增支持自定義結果校驗器
  • 結果校驗器中實現變量引用
  • 結果校驗內容新增支持正則表達式提取

改造結果

具體的改造過程就不寫了,有興趣的同窗能夠直接閱讀源碼,重點查看httprunner/context.py中的parse_validatordo_validationvalidate三個函數。

通過優化後,改造目標中的三項功能都實現了。爲了更好地展示改造後的結果校驗器,此處將結合實例進行演示。

新增支持自定義結果校驗器

先來看第一個優化項,新增支持自定義結果校驗器。

假設咱們須要使用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_codeHttpRunner內置的校驗方法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中,checkexpect都可實現實現變量的引用;而引用的變量,能夠來自四種類型:

  • 當前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"]
複製代碼

固然,這次優化保證了與歷史版本的兼容,以前編寫的測試用例腳本的運行是徹底不會受到任何影響的。

相關文章
相關標籤/搜索