在 pytest yield fixture 中使用 request.function 时出现 AttributeError

AttributeError when using request.function in pytest yield fixture

我有几个 pytest 测试用例需要几乎相同的设置,所以我想让它们重用一个夹具来保持干燥。该设置涉及在外部票证跟踪系统中创建新票证,然后测试用例根据数据与票证交互,最后通过关闭票证来清理夹具。这里的挑战是每个测试用例需要在工单中准备略有不同的数据。

每个测试用例都有不同的调用和不同的断言,所以我无法将它们全部组合成一个具有单个测试夹具的参数化测试用例。对夹具本身进行参数化会导致每个测试用例 运行 夹具数据的每个排列,最终导致大量不相关的测试失败。

我想做的是在测试用例中设置一个变量,然后让夹具在创建工单时使用该变量来设置测试数据。我已尝试使用 pytest fixture docs 中指定的 request.function 但我不断收到:

=================================== ERRORS ===================================
    ____________________ ERROR at setup of TestMCVE.test_stuff ___________________

request = <SubRequest 'ticket' for <Function 'test_stuff'>>

    @pytest.yield_fixture
    def ticket(request):
>       ticket_summary = getattr(request.function, "summary")
E       AttributeError: 'function' object has no attribute 'summary'

tests\test_mcve.py:11: AttributeError

我的代码是:

import pytest


def ticket_system_api(summary):
    # stub for MCVE purposes
    return summary


@pytest.yield_fixture
def ticket(request):
    ticket_summary = getattr(request.function, "summary")
    new_ticket = ticket_system_api(summary=ticket_summary)
    yield new_ticket


class TestMCVE:
    def test_stuff(self, ticket):
        summary = 'xyz'
        # do real things here, except MCVE
        assert 'xyz' == ticket

我已经尝试使用 request.node 而不是 request.function 以及 binding the summary variable per this answer,将 summary = 'xyz' 更改为 test_stuff.summary = 'xyz' 但这些仍然失败属性错误。

如何将功能级数据传递给灯具?

您可以使用 indirect parametrization 完成此操作。 API(和文档)可能更友好,但您想要的功能就在那里。

您的示例非常接近,需要进行一些小的调整。看一看:

import pytest


def ticket_system_api(summary):
    # stub for MCVE purposes
    return summary


@pytest.fixture
def ticket(request):
    # NOTE: This will raise `AttributeError` if the fixture
    # doesn't receive a parameter.
    ticket_summary = request.param
    new_ticket = ticket_system_api(summary=ticket_summary)
    return new_ticket


class TestMCVE:
    @pytest.mark.parametrize('ticket', ('abc',), indirect=True)
    def test_abc(self, ticket):
        # do real things here, except MCVE
        assert ticket == 'abc'

    @pytest.mark.parametrize('ticket', ('xyz',), indirect=True)
    def test_xyz(self, ticket):
        # do real things here, except MCVE
        assert ticket == 'xyz'