首頁 > 軟體

pytest fixtures函數及測試函數的引數化解讀

2022-05-31 18:00:17

pytest fixtures測試函數引數化

Pytest會在以下幾個級別啟用測試引數化:

  • pytest.fixture(),可以對fixture函數進行引數化。
  • @pytest.mark.parametrize,可以在測試函數或類中定義多組引數和fixture。
  • pytest_generate_tests,可以自定義引數化方案或擴充套件。

一、@pytest.mark.parametrize:引數化測試函數

1. 常規用法

對測試函數的引數進行引數化,直接使用內建的裝飾器pytest.mark.parameterized即可。

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

從程式碼裡可以看出,在裝飾器裡定義了三個不同的元組。我們把("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
拆開看:

  • "test_input,expected":這個字串裡定義了2個引數,test_input和expected。
  • ("3+5", 8), ("2+4", 6), ("6*9", 42):這裡3個元組,沒個元組裡有2個元素,依次序分別對應test_input和expected。
  • 3個元組外層的[]:列表裡就是引數化具體的傳參了,因為裡面傳了3個不同的元組,所以測試函數test_eval會分別執行3次。
============================= test session starts =============================
platform win32 -- Python 3.9.4, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: D:PycharmProjectswms-apiinterface, configfile: pytest.inicollected 3 items
test_module1.py ..F
demotest_module1.py:3 (test_eval[6*9-42])
54 != 42
Expected :42
Actual   :54
 <Click to see difference>
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_module1.py:6: AssertionError

執行結果可以看到最後一次失敗了,因為第三次執行測試函數取的引數是 ("6*9", 42),54不等於42,所以斷言失敗。

2. 在引數化中標記單個測試範例

在引數化中標記單個測試範例,比如之前提到過的mark.xfail,這個可以標記測試函數為失敗。那麼在引數化中,如果想讓其中的某個引數執行
的時候測試失敗,就可以這樣用:

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

執行一下:

test_module1.py                                                       [100%]
======================== 2 passed, 1 xfailed in 0.05s =========================..x

3. 多個引數化組合,笛卡爾積

如果在測試函數上加了多個引數化裝飾器,那麼得到的引數組合是一個笛卡爾積:

import pytest
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_foo(x, y):
    print("nx:", x)
    print("y:", y)

應該會組合成4組資料x=0/y=2, x=1/y=2, x=0/y=3, 和x=1/y=3,測試函數執行4次:

test_module1.py .
x: 0
y: 2
.
x: 1
y: 2
.
x: 0
y: 3
.
x: 1
y: 3
                                                     [100%]
============================== 4 passed in 0.01s ==============================

二、用勾點函數pytest_generate_tests example拓展

如果有些場景需要動態的確定引數或者fixture的使用範圍,那麼可以使用pytest_generate_tests這個勾點函數,該函數會在收集測試函數時候被呼叫。

通過傳入的metafunc物件,可以檢查請求測試函數的上下文,還可以進一步的呼叫metafunc.parameterize()來實現引數化。

舉例,有個測試函數需要接受輸入的字串作為引數,而且通過pytest命令列獲取到,那麼就要編寫一個獲取引數的fixture函數來給測試函數呼叫。

# content of test_strings.py
def test_valid_string(stringinput):
    assert stringinput.isalpha()

新建conftest.py檔案,fixture函數寫在這裡:

# 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"))

現在用命令列方式來執行這個測試函數:

pytest -q --stringinput="hello" --stringinput="world" test_strings.py

會執行2次。

D:PycharmProjectswms-apiinterfacedemo>pytest -q --stringinput="hello" --stringinput="world" test_strings.py
..                                                                                                                                                                     [100%]
2 passed in 0.01s

再換個輸入引數,讓測試函數失敗:

pytest -q --stringinput="!" test_strings.py

FAILED test_strings.py::test_valid_string[!] - AssertionError: assert False1 failed in 0.04s

如果沒有字串輸入,那麼測試函數它將被跳過。因為metafunc.parameterize()被呼叫時&#xff0c;傳過去的是一個列表:

pytest -q -rs test_strings.py

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時, 如果使用不同的引數集進行多次引數化,這些引數集上的所有引數名稱都不能重複,否則將會報錯。

總結

文中講到的3種用法,實際應用中第一種最常見,第二種次之,至於第三種,可以作為一些用法啟發,更多關於pytest fixtures測試函數引數化的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com