test

自动化测试框架pytest教程16-高级参数化

2023-01-19  本文已影响0人  python测试开发

使用复杂的值

有时你可能想用数据结构或对象作为参数化的值。

ch16/test_ids.py

@pytest.mark.parametrize(
    "starting_card",
    [
        Card("foo", state="todo"),
        Card("foo", state="in prog"),
        Card("foo", state="done"),
    ],
)
def test_card(cards_db, starting_card):
    index = cards_db.add_card(starting_card)
    cards_db.finish(index)
    card = cards_db.get_card(index)
    assert card.state == "done"
$ pytest -v test_ids.py::test_card
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=3603486280
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... collected 3 items

test_ids.py::test_card[starting_card1] PASSED                            [ 33%]
test_ids.py::test_card[starting_card2] PASSED                            [ 66%]
test_ids.py::test_card[starting_card0] PASSED                            [100%]

============================== 3 passed in 0.22s ==============================

对于那些没有明显字符串值的对象,pytest会对其进行编号。"starting_card0","starting_card1",等等。

创建自定义标识符

你可以通过使用ids参数定义函数来生成标识符。通常情况下,内置的str或repr函数可以正常工作。

让我们尝试使用str作为一个ID函数。

ch16/test_ids.py

card_list = [
    Card("foo", state="todo"),
    Card("foo", state="in prog"),
    Card("foo", state="done"),
]

@pytest.mark.parametrize("starting_card", card_list, ids=str)
def test_id_str(cards_db, starting_card):
    ...
    index = cards_db.add_card(starting_card)
    cards_db.finish(index)
    card = cards_db.get_card(index)
    assert card.state == "done"

这里我们添加了ids=str。


$ pytest -v test_ids.py::test_id_str
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=3244471004
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... collected 3 items

test_ids.py::test_id_str[Card(summary='foo', owner=None, state='done', id=None)] PASSED [ 33%]
test_ids.py::test_id_str[Card(summary='foo', owner=None, state='in prog', id=None)] PASSED [ 66%]
test_ids.py::test_id_str[Card(summary='foo', owner=None, state='todo', id=None)] PASSED [100%]

============================== 3 passed in 0.19s ==============================

让我们来定义我们自己的ID函数。它需要接收Card对象并返回字符串。而我们将把id设置为我们的新函数。

ch16/test_ids.py

def card_state(card):
    return card.state

@pytest.mark.parametrize("starting_card", card_list, ids=card_state)
def test_id_func(cards_db, starting_card):
    ...
    index = cards_db.add_card(starting_card)
    cards_db.finish(index)
    card = cards_db.get_card(index)
    assert card.state == "done"
$ pytest -v test_ids.py::test_id_func
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=1314635729
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... collected 3 items

test_ids.py::test_id_func[done] PASSED                                   [ 33%]
test_ids.py::test_id_func[in prog] PASSED                                [ 66%]
test_ids.py::test_id_func[todo] PASSED                                   [100%]

============================== 3 passed in 0.18s ==============================

许多ID函数会很短。如果是单行函数,用lambda函数效果很好。

ch16/test_ids.py

@pytest.mark.parametrize(
    "starting_card", card_list, ids=lambda c: c.state
)
def test_id_lambda(cards_db, starting_card):
    ...
    index = cards_db.add_card(starting_card)
    cards_db.finish(index)
    card = cards_db.get_card(index)
    assert card.state == "done"

输出将看起来是一样的。

ids功能在参数化的固定程序和pytest_generate_tests中也是可用的。还有两种方法可以创建自定义标识符:pytest.param和id列表。

在pytest.param中添加一个ID
在标记文件、类和参数中,我们用pytest.param为参数化的值添加标记。pytest.param也可以用来添加ID。在下面的例子中,我们将为一个参数添加一个 "特殊 "的ID。

ch16/test_ids.py
c_list = [
Card("foo", state="todo")。
" pytest.param(Card("foo", state="in prog"), id="special") 。
Card("foo", state="done"),
]

@pytest.mark.parametrize("starting_card", c_list, ids=card_state)
def test_id_param(card_db, starting_card):
...
这个方法在与其他方法结合时特别有用。在这个例子中,我们用pytest.param指定了一个 "特殊 "的ID,并让ids=cards_state()生成其他的ID。

结果测试运行看起来像这样。

$ pytest -v test_ids.py::test_id_param
========================= 测试会话开始 ==========================
收集了3个项目

test_ids.py::test_id_param[todo] PASSED [ 33%]
test_ids.py::test_id_param[special] PASSED [ 66%]
test_ids.py::test_id_param[done] PASSED [100%]

========================== 3在0.02s内通过 ===========================
如果你只有一两个需要特殊处理的ID,使用pytest.param是非常好的。如果你想手工编写所有的ID,pytest.param会很麻烦。如果你想为所有的值编写自定义的ID,使用一个列表可能更容易维护。

你可以给ids提供一个列表,而不是函数。

ch16/test_ids.py

id_list = ["todo", "in prog", "done"]


@pytest.mark.parametrize("starting_card", card_list, ids=id_list)
def test_id_list(cards_db, starting_card):
    ...
    index = cards_db.add_card(starting_card)
    cards_db.finish(index)
    card = cards_db.get_card(index)
    assert card.state == "done"

你必须格外小心,以保持列表的同步。否则,ID就会出错。保持ID和值在一起的一个方法是使用ID作为字典的键。然后你可以用 .keys() 作为 ID 的列表,用 .values() 作为参数的列表。当ID不容易用函数生成时,以这种方式使用字典特别有用。

text_variants = {
    "Short": "x",
    "With Spaces": "x y z",
    "End In Spaces": "x    ",
    "Mixed Case": "SuMmArY wItH MiXeD cAsE",
    "Unicode": "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾",
    "Newlines": "a\nb\nc",
    "Tabs": "a\tb\tc",
}


@pytest.mark.parametrize(
    "variant", text_variants.values(), ids=text_variants.keys()
)
def test_summary_variants(cards_db, variant):
    i = cards_db.add_card(Card(summary=variant))
    c = cards_db.get_card(i)
    assert c.summary == variant

使用动态值

ch16/test_param_gen.py

import pytest
from cards import Card

def text_variants():
    variants = {
        "Short": "x",
        "With Spaces": "x y z",
        "End in Spaces": "x    ",
        "Mixed Case": "SuMmArY wItH MiXeD cAsE",
        "Unicode": "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾",
        "Newlines": "a\nb\nc",
        "Tabs": "a\tb\tc",
    }
    for key, value in variants.items():
        yield pytest.param(value, id=key)


@pytest.mark.parametrize("variant", text_variants())
def test_summary(cards_db, variant):
    i = cards_db.add_card(Card(summary=variant))
    c = cards_db.get_card(i)
    assert c.summary == variant

$ pytest test_param_gen.py -v
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=1784156481
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... collected 7 items

test_param_gen.py::test_summary[Mixed Case] PASSED                       [ 14%]
test_param_gen.py::test_summary[Tabs] PASSED                             [ 28%]
test_param_gen.py::test_summary[Newlines] PASSED                         [ 42%]
test_param_gen.py::test_summary[End in Spaces] PASSED                    [ 57%]
test_param_gen.py::test_summary[Short] PASSED                            [ 71%]
test_param_gen.py::test_summary[Unicode] PASSED                          [ 85%]
test_param_gen.py::test_summary[With Spaces] PASSED                      [100%]

============================== 7 passed in 0.41s ==============================

使用多个参数

ch16/test_multiple.py

from cards import Card

summaries = ["short", "a bit longer"]
owners = ["First", "First M. Last"]
states = ["todo", "in prog", "done"]


@pytest.mark.parametrize(
    "summary, owner, state",
    [
        ("short", "First", "todo"),
        ("short", "First", "in prog"),
        # ...
    ],
)
def test_add_lots(cards_db, summary, owner, state):
    """Make sure adding to db doesn't change values."""
    i = cards_db.add_card(Card(summary, owner=owner, state=state))
    card = cards_db.get_card(i)

    expected = Card(summary, owner=owner, state=state)
    assert card == expected


@pytest.mark.parametrize("state", states)
@pytest.mark.parametrize("owner", owners)
@pytest.mark.parametrize("summary", summaries)
def test_stacking(cards_db, summary, owner, state):
    """Make sure adding to db doesn't change values."""
    ...
    expected = Card(summary, owner=owner, state=state)
    i = cards_db.add_card(Card(summary, owner=owner, state=state))
    card = cards_db.get_card(i)
    assert card == expected

$ pytest test_multiple.py::test_add_lots -v
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=3994018912
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... collected 2 items

test_multiple.py::test_add_lots[short-First-in prog] PASSED              [ 50%]
test_multiple.py::test_add_lots[short-First-todo] PASSED                 [100%]

============================== 2 passed in 0.15s ==============================

$ pytest test test_multiple.py::test_stacking -v
============================= test session starts =============================
platform win32 -- Python 3.9.13, pytest-7.1.2, pluggy-1.0.0 -- D:\ProgramData\Anaconda3\python.exe
cachedir: .pytest_cache
Using --randomly-seed=1036957166
rootdir: D:\code\pytest_quick, configfile: pytest.ini
plugins: allure-pytest-2.12.0, Faker-4.18.0, tep-0.8.2, anyio-3.5.0, cov-4.0.0, randomly-3.12.0, repeat-0.9.1, xdist-3.1.0
collecting ... ERROR: file or directory not found: test

collected 0 items

============================ no tests ran in 0.05s ============================

上一篇 下一篇

猜你喜欢

热点阅读