目錄:
- 安裝及入門
- 使用和調用方法
- 原有TestSuite使用方法
- 斷言的編寫和報告
- Pytest fixtures:清晰 模塊化 易擴展
- 使用Marks標記測試用例
- Monkeypatching/對模塊和環境進行Mock
- 使用tmp目錄和文件
- 捕獲stdout及stderr輸出
- 捕獲警告信息
- 模塊及測試文件中集成doctest測試
- skip及xfail: 處理不能成功的測試用例
- Fixture方法及測試用例的參數化
- 緩存: 使用跨執行狀態
- unittest.TestCase支持
- 運行Nose用例
- 經典xUnit風格的setup/teardown
- 安裝和使用插件
- 插件編寫
- 編寫鉤子(hook)方法
- 運行日志
- API參考
- 優質集成實踐
- 片狀測試
- Pytest導入機制及sys.path/PYTHONPATH
- 配置選項
- 示例及自定義技巧
- Bash自動補全設置
Fixture方法及測試用例的參數化
pytest在多個級別啟用測試參數化:
@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]
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
命令行選項設置字符串輸入。讓我們首先編寫一個接受stringinput
fixture函數參數的簡單測試:
# 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
使用不同的參數集多次調用時,這些集合中的所有參數名稱都不能重復,否則將引發錯誤。
更多例子
有關更多示例,你可能需要查看更多參數化示例。