Fixture方法及測試用例的參數化
Pytest在多個級別啟用測試參數化:
pytest.fixture()允許一個[參數化Fixture方法。- @pytest.mark.parametrize允許在測試函數或類中定義多組參數和Fixture。
- pytest_generate_tests允許用戶定義自定義參數化方案或擴展。
@pytest.mark.parametrize:參數化測試函數
2.2版中的新函數。
版本2.4中的更改:一些改進。
內置的pytest.mark.parametrize裝飾器支持測試函數的參數化。以下是測試函數的示例,該函數實現檢查某個輸入是否導致預期輸出:
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",42)])
def test_eval(test_input,expected):
assert eval(test_input) == expected
這里,@parametrize裝飾器定義了三個不同的參數,test_input,expected組成元組,以便test_eval函數依次使用它們運行三次:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y,pytest-4.x.y,py-1.x.y,pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..F [100%]
================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________
test_input = '6*9',expected = 42
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+4",6),("6*9",42)])
def test_eval(test_input,expected):
> assert eval(test_input) == expected
E AssertionError: assert 54 == 42
E + where 54 = eval('6*9')
test_expectation.py:6: AssertionError
==================== 1 failed,2 passed in 0.12 seconds ====================
注意: 默認情況下,pytest會轉義unicode字符串中用於參數化的任何非ascii字符,因為它有幾個缺點。但是,如果你想在參數化中使用unicode字符串並在終端中按原樣(非轉義)查看它們,請在以下位置使用此選項
pytest.ini:
(譯者注:需要pytest>=5.0.0, 數據或ids中的中文才能正常顯示)
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
但請記住,這可能會導致不必要的副作用甚至是錯誤,具體取決於所使用的操作系統和當前安裝的插件,因此使用它需要你自擔風險。
如本例所示,只有一對輸入/輸出值無法通過簡單的測試用例。和通常的測試函數參數一樣,你可以在traceback中看到input和output值。
請注意,你還可以在類或模塊上使用參數化標記(請參閱[使用屬性標記測試函數),這將使用參數集調用多個函數。
也可以在參數化中標記單個測試實例,例如使用內置mark.xfail:
# content of test_expectation.py
import pytest
@pytest.mark.parametrize(
"test_input,expected",
[("3+5",8),("2+4",6),pytest.param("6*9",42,marks=pytest.mark.xfail)],
)
def test_eval(test_input,expected):
assert eval(test_input) == expected
我們運行這個:
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y,pytest-4.x.y,py-1.x.y,pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items
test_expectation.py ..x [100%]
=================== 2 passed,1 xfailed in 0.12 seconds ====================
之前導致失敗的一個參數集現在顯示為“xfailed(預期失敗)”測試。
如果提供的值parametrize導致空列表 - 例如,如果它們是由某個函數動態生成的 - 則pytest的行為由該empty_parameter_set_mark選項定義。
要獲得多個參數化參數的所有組合,你可以堆疊parametrize裝飾器:
import pytest
@pytest.mark.parametrize("x",[0,1])
@pytest.mark.parametrize("y",[2,3])
def test_foo(x,y):
pass
這將運行與設定參數的測試x=0/y=2,x=1/y=2,x=0/y=3,並x=1/y=3在裝飾的秩序排氣參數。
基本的pytest_generate_tests例子
有時你可能希望實現自己的參數化方案或實現一些動力來確定Fixture的參數或范圍。為此,你可以使用pytest_generate_tests在收集測試函數時調用的鈎子。通過傳入的metafunc對象,你可以檢查請求的測試上下文,最重要的是,你可以調用metafunc.parametrize()以引起參數化。
例如,假設我們想要運行一個測試,我們想通過一個新的pytest命令行選項設置字符串輸入。讓我們首先編寫一個接受stringinputfixture函數參數的簡單測試:
# 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.12 seconds
讓我們運行一個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
1 failed in 0.12 seconds
正如所料,我們的測試用例失敗。
如果你沒有指定stringinput,它將被跳過,因為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.12 seconds
注意:
metafunc.parametrize使用不同的參數集多次調用時,這些集合中的所有參數名稱都不能重復,否則將引發錯誤。
更多示例
有關更多示例,你可能需要查看更多參數化示例。
