接口自動化測試實現:
pytest + request + allure
V1.0 版本 初探 API 自動化測試
寫出來的自動化腳本存在一些問題,針對遇到的問題進行了部分優化,問題記錄可查看該文章:
項目預覽
修改記錄
pytest 替代 unittest
conftest.py
用例內必要的參數可以封裝到 conftest.py
內,方便調用,比如:
- host
- 公共參數
- 特殊參數
- 獲取配置信息
- …
以公共參數爲例:
# conftest.py
@pytest.fixture(scope="module")
def common_params():
""" 獲取公共參數
"""
return get_params_ini.GetParamsIni().get_params()
那麼在用例內就可以直接使用,省去了之前每條 case 模塊 setup
獲取公共參數及 host 的冗餘。
# testcase.py
import pytest
def test_case_02(self, common_params):
print(common_params)
pytest.skip("此爲測試啓動方法")
參數化
pytest 還有一大亮點是參數化,這樣只用設計用例數據就可以啦。
@pytest.mark.parametrize(
"num, result",
[(-1, "fail"), (5, "pass"), (0, "error")]
)
def test_case_01(self, num, result):
if num > 0:
assert result == "pass"
elif num < 0:
assert result == "fail"
else:
assert result == "error"
指定用例
pytest.main(["-q", "-s", 用例模塊1, 用例模塊2])
日誌記錄
重新封裝 logging
模塊,記錄每次運行用例的詳細信息
class Logging:
"""setup logging
Examples:
>>> import logging
>>> Logging()
>>> logging.debug("this is debug message")
/log/2020-04-30.log
[DEBUG - 2020-04-30 15:26:40,033 - logger.py] : this is debug message
"""
斷言器
def test_case_01(self, get_api, common_params):
response = request.get(url=get_api, params=common_params)
result = response.json()
if response.raise_for_status():
# 1. 首次生成 case,需要運行一次獲取到 response,生成 validator
validate.gen_validator(file_path, result)
# 2. 生成 validator 成功後,取消掉註釋,即可正常運行斷言器
# validate.run_validator(validator, result)
上面就是寫出來的case,運行步驟如下:
- 請求接口,獲取到響應實體(JSON);
- 判斷狀態碼爲 200,證明接口請求成功;
- 根據響內容生成斷言器(validator),填充在該 py 文件頭部;
- 修改斷言器的內容,取消代碼中的第2步
# validate.run_validator(validator, result)
註釋; - 再次運行,即可按照斷言器的預期是做比較。
什麼是斷言器?
拿請求版本更新的接口返回值爲例:
{
"success": true,
"errorlog": "版本無更新。"
}
一般我們這樣斷言:
assert result["succese"] == True
assert result["errorlog"] == "版本無更新。"
但是當接口字段非常多的時候,會寫非常多的assert來校驗,接口字段變動,也會很難維護。所以根據響應值生成一個斷言器,斷言器就是將RESPONSE
轉爲一個容易維護的數據格式:
validator = {
"errorlog": {
"actual": "",
"compare": am.regex_match(),
"expect": "版本.*。$"
},
"success": {
"actual": "",
"compare": am.is_boolean(),
"expect": "false"
}
}
將每個字段的值拆分爲
- 實際值(actual)
- 斷言方式(compare)
- 預期值(expect)
斷言器生成後會將請求的響應值自動填入預期內,作爲判斷的標準。
部分字段需要修改,可以在 validator
中修改 compare 和 expect 。
上面例子的斷言內容爲:
- errorlog 字段使用正則表達式判斷是否以“版本”兩個字開始,以句號結尾。可以模糊斷言“版本有更新。”和“版本無更新。”的情況。
- success 字段爲布爾值,通過
is_boolean()
方法判斷返回值的類型,再與預期做比較。
判斷的方法還有很多,基本都做了封裝。
allure
Allure框架是一種靈活的輕量級多語言測試報告工具,它不僅能夠以簡潔的web報告形式顯示已測試的內容,而且允許參與開發過程的每個人從測試的日常執行中提取最大限度的有用信息。
pytest 支持 allure 生成報告,使用方便。
使用方法:
@allure.feature # 用於定義被測試的功能模塊
@allure.story # 用於定義被測功能的用戶場景
@allure.title # 用於定義用例名稱
@allure.severity # 用於定義用例優先級
@allure.issue # 用於定義問題表識,關聯標識已有的問題,可爲一個url鏈接地址
@allure.testcase # 用於用例標識,關聯標識用例,可爲一個url鏈接地址
@allure.attach # 用於向測試報告中輸入一些附加的信息,通常是一些測試數據信息
@allure.step # 用於將一些通用的函數作爲測試步驟輸出到報告,調用此函數的地方會向報告中輸出步驟
所以最後的用例是這樣子的:
import pytest
import allure
from common import request
from common import validate
from common.comparators import AssertMethods as am
file_path = __file__
validator = {
"errorlog": {
"actual": "",
"compare": am.regex_match(),
"expect": "版本.*。$"
},
"success": {
"actual": "",
"compare": am.is_boolean(),
"expect": "false"
}
}
@allure.feature("appControl")
@allure.story("驗證版本更新")
class TestCheckVersion:
@allure.title("驗證版本無更新情況")
@allure.severity(allure.severity_level.NORMAL)
def test_case_01(self, get_api, common_params):
with allure.step("第一步:發送請求,獲取響應實體"):
response = request.get(url=get_api, params=common_params)
result = response.json()
allure.attach(response.url, "請求", allure.attachment_type.TEXT)
allure.attach(response.text, "響應", allure.attachment_type.TEXT)
with allure.step("第二步:運行斷言器"):
if validate.assert_code(response.status_code, 200):
# 1. 首次生成 case,需要運行一次獲取到 response,生成 validator
validate.gen_validator(file_path, result)
validator["errorlog"]["expect"] = "版本無更新。" # 修改斷言器內的值
# 2. 生成 validator 成功後,取消掉註釋,即可正常運行斷言器
validate.run_validator(validator, result)
def test_case_02(self):
pytest.skip("驗證版本有更新。")
if __name__ == '__main__':
pytest.main()
生成的allure報告展示如下:
自動生成用例模版
寫用例過程中發現其實上面完成的那條用例,大部分代碼是可以複用在其他用例上的。
如:alllure 裝飾器的調用,接口請求值的獲取,每條用例都是一樣的。
因爲有固定的模版,所有就可以根據接口名稱來生成測試用例。
def generate_case(feature, api_name):
""" 模塊、接口名稱
Example:
>>> generate_case("appControl", "checkVersion")
1. 在case目錄下,創建 appControl 文件夾,
2. 文件夾內創建 test_checkVersion.py 文件
3. 文件內寫入模版 case
"""
pass
通過這種方式,還可以實現批量新建case模版,將所有的接口按模塊寫入到yml文件內,遍歷該 yml 文件,生成全部case,然後再逐條調試。
# all_api.yml
appControl:
check_version.m
test_api.m
feature:
feature_test_api.m
生成後效果如下:
case/
appControl/
test_check_version.py
test_test_api.py
feature/
test_feature_test_api.py
寫在最後
目前已經提交到 GitHub,還存在諸多不足,慢慢優化。