pytest 核心功能-參數(shù)化fixtures和測試函數(shù)

2022-03-21 09:41 更新

Pytest可以在幾個級別上實現(xiàn)測試參數(shù)化:

  • ?pytest.fixture()?允許對?fixture?函數(shù)進行參數(shù)化。
  • ?@pytest.mark.parametrize?允許在測試函數(shù)或類中定義多組實參和?fixture?。

  • ?Pytest_generate_tests?允許定義自定義參數(shù)化方案或擴展。

@pytest.mark.parametrize:參數(shù)化測試函數(shù)

內(nèi)置的 ?pytest.mark.parametrize? 裝飾器可以對測試函數(shù)的參數(shù)進行參數(shù)化。 下面是一個典型的測試函數(shù)示例,它實現(xiàn)了檢查某個輸入是否會導(dǎo)致預(yù)期的輸出:

# 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 ?函數(shù)將依次使用它們運行 3 次:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
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
========================= short test summary info ==========================
FAILED test_expectation.py::test_eval[6*9-42] - AssertionError: assert 54...
======================= 1 failed, 2 passed in 0.12s ========================

注意:參數(shù)值按原樣傳遞給測試(沒有任何副本)。

例如,如果您將一個列表或dict作為參數(shù)值傳遞,并且測試用例代碼對它進行了更改,那么這些更改將在隨后的測試用例調(diào)用中反映出來。

Pytest默認(rèn)情況下轉(zhuǎn)義用于參數(shù)化的unicode字符串中的任何非ascii字符,因為它有幾個缺點。但是,如果你想在參數(shù)化中使用unicode字符串,并在終端中看到它們是(非轉(zhuǎn)義的),在你的?pytest.ini?中使用這個選項:

[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True

但請記住,根據(jù)所使用的操作系統(tǒng)和當(dāng)前安裝的插件,這可能會導(dǎo)致不必要的副作用,甚至bug,所以使用它的風(fēng)險由您自己承擔(dān)。

正如本例中所設(shè)計的,只有一對輸入/輸出值不能通過簡單的測試函數(shù)。和通常的測試函數(shù)參數(shù)一樣,您可以在回溯中看到輸入和輸出值。

注意,你也可以在類或模塊上使用參數(shù)化標(biāo)記,它會調(diào)用幾個帶有參數(shù)集的函數(shù),例如:

import pytest


@pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
class TestClass:
    def test_simple_case(self, n, expected):
        assert n + 1 == expected

    def test_weird_simple_case(self, n, expected):
        assert (n * 1) + 1 == expected

要參數(shù)化模塊中的所有測試,你可以給全局變量?pytestmark?賦值:

import pytest

pytestmark = pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])


class TestClass:
    def test_simple_case(self, n, expected):
        assert n + 1 == expected

    def test_weird_simple_case(self, n, expected):
        assert (n * 1) + 1 == expected

也可以在參數(shù)化中標(biāo)記單個測試實例,例如使用內(nèi)置的?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

運行結(jié)果如下:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 3 items

test_expectation.py ..x                                              [100%]

======================= 2 passed, 1 xfailed in 0.12s =======================

之前導(dǎo)致失敗的一個參數(shù)集現(xiàn)在顯示為?xfailed?(預(yù)期失敗)測試。

如果提供給參數(shù)化的值導(dǎo)致一個空列表——例如,如果它們是由某個函數(shù)動態(tài)生成的——pytest的行為是由?empty_parameter_set_mark?選項定義的。

要獲得多個參數(shù)化參數(shù)的所有組合,你可以堆棧參數(shù)化裝飾器:

import pytest


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

這將使用設(shè)置為 x=0/y=2、x=1/y=2、x=0/y=3 和 x=1/y=3 的參數(shù)運行測試,并按裝飾器的順序耗盡參數(shù)。

基本 pytest_generate_tests 示例

有時,您可能希望實現(xiàn)自己的參數(shù)化方案,或者實現(xiàn)一些動態(tài)機制,以確定參數(shù)或?fixture?的范圍。為此,您可以使用?pytest_generate_tests?鉤子,它在收集測試函數(shù)時被調(diào)用。通過傳入的?metafunc?對象,你可以檢查請求的測試上下文,最重要的是,你可以調(diào)用?metafunc.parameterize()?來進行參數(shù)化。

假設(shè)我們想運行一個測試,接受我們想通過一個新的pytest命令行選項設(shè)置的字符串輸入。讓我們首先編寫一個接受?stringinput fixture?函數(shù)參數(shù)的簡單測試:

# content of test_strings.py


def test_valid_string(stringinput):
    assert stringinput.isalpha()

現(xiàn)在,我們添加了一個?conftest.py?文件,其中包含了一個命令行選項和測試函數(shù)的參數(shù)化:

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

如果我們現(xiàn)在傳遞兩個?stringinput?值,我們的測試將運行兩次:

$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
..                                                                   [100%]
2 passed in 0.12s

讓我們也運行一個?stringinput?,這會導(dǎo)致測試失敗:

$ 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 0xdeadbeef0001>()
E        +    where <built-in method isalpha of str object at 0xdeadbeef0001> = '!'.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

正如預(yù)期的那樣,我們的測試函數(shù)失敗了。

如果你沒有指定?stringinput?,它將被跳過,因為?metafuncc.parameterize()?將被調(diào)用,參數(shù)列表為空:

$ 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 /home/sweet/project/test_strings.py:2
1 skipped in 0.12s

請注意,當(dāng)調(diào)用?metafunc?使用不同的參數(shù)集進行多次參數(shù)化,這些參數(shù)集中的所有參數(shù)名不能重復(fù),否則將引發(fā)錯誤。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號