前言
pytest 實現參數化有三種方式
- pytest.fixture() 使用 fixture 傳 params 參數實現參數化
- @ pytest.mark.parametrize 允許在測試函數或類中定義多組參數,在用例中實現參數化
- pytest_generate_tests 允許定義自定義參數化方案或擴展。
pytest_generate_tests
pytest_generate_tests 在測試用例參數化收集前調用此鈎子函數,根據測試配置或定義測試函數的類或模塊中指定的參數值生成測試用例,
可以使用此鈎子實現自定義參數化方案或擴展,相關文檔參考官方文檔https://docs.pytest.org/en/latest/parametrize.html#pytest-generate-tests
有時您可能想要實現自己的參數化方案或實現某種動態性來確定 fixture 的參數或范圍。為此,可以使用pytest_generate_tests在收集測試函數時調用的鈎子。通過傳入的 metafunc 對象,您可以檢查請求的測試上下文,最重要的是,您可以調用 metafunc.parametrize() 引起參數化。
例如,假設我們要運行一個測試,並接受要通過新的 pytest 命令行選項設置的字符串輸入。讓我們首先編寫一個接受 stringinput 函數參數的簡單測試:
# content of test_strings.py
def test_valid_string(stringinput):
assert stringinput.isalpha()
現在,我們添加一個conftest.py文件,其中包含命令行選項和測試函數的參數化:
# content of conftest.py
def pytest_addoption(parser):
parser.addoption(
"--stringinput",
action="append",
default=[],
help="list of stringinputs to pass to test functions",
)
def pytest_generate_tests(metafunc):
if "stringinput" in metafunc.fixturenames:
metafunc.parametrize("stringinput", metafunc.config.getoption("stringinput"))
如果現在傳遞兩個stringinput值,則測試將運行兩次:
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
.. [100%]
2 passed in 0.12s
我們還使用一個stringinput運行,這將導致測試失敗:
$ pytest -q --stringinput="!" test_strings.py
F [100%]
================================= FAILURES =================================
___________________________ test_valid_string[!] ___________________________
stringinput = '!'
def test_valid_string(stringinput):
> assert stringinput.isalpha()
E AssertionError: assert False
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
test_strings.py:4: AssertionError
========================= short test summary info ==========================
FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False
1 failed in 0.12s
不出所料,我們的測試功能失敗。
如果您未指定字符串輸入,則將跳過它,因為 metafunc.parametrize()將使用空參數列表來調用它:
$ pytest -q -rs test_strings.py
s [100%]
========================= short test summary info ==========================
SKIPPED [1] test_strings.py: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:2
1 skipped in 0.12s
請注意,當metafunc.parametrize使用不同的參數集多次調用時,這些集合中的所有參數名稱都不能重復,否則會引發錯誤。
更多的參數化案例參考https://docs.pytest.org/en/latest/example/parametrize.html#paramexamples
使用示例
在 conftest.py 自定義參數化的鈎子, 判斷當測試用例傳了 param 參數,就讓它生成參數化的用例
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
if "param" in metafunc.fixturenames:
metafunc.parametrize("param",
metafunc.module.test_data,
ids=metafunc.module.names,
scope="function")
test_parm.py相關的測試數據和用例內容,names是用例的名稱,test_data是測試數據
import requests
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/
# 用例的id名稱
names = ["login nam1", "login name2"]
# 測試數據 list of dict
test_data = [{
"url": "http://49.235.x.x:5000/api/v1/login/",
"method": "POST",
"headers":
{
"Content-Type": "application/json"
},
"json":
{
"username": "test",
"password": "123456"
}
},
{
"url": "http://49.235.x.x:5000/api/v1/login/",
"method": "POST",
"headers":
{
"Content-Type": "application/json"
},
"json":
{
"username": "test",
"password": "123456"
}
}
]
def test_login(param):
r = requests.session().request(**param)
print(r.text)
這樣運行會,自動生成2條測試用例
================= test session starts ===============
platform win32 -- Python 3.6.6, pytest-4.5.0, py-1.9.0, pluggy-0.13.1
rootdir: D:\
collected 2 items
..\test_param.py
{"code": 0, "msg": "login success!", "username": "test", "token": "81ae10099feaca6f0af5ba122444bea2a83a2dc9"}
.{"code": 0, "msg": "login success!", "username": "test", "token": "1ccfb99fd68f22da66c95660702af22d64108067"}
.
=============== 2 passed in 0.62 seconds ===============
上面是把測試數據和代碼放一起,想實現代碼和數據分離的話,上面的 names 和 test_data 測試數據寫到 yaml 文件,寫個函數去讀取 yaml 文件的數據。