我有几个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',但是它们仍然失败,并带有相同的AttributeError。

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

最佳答案

您可以使用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'

10-08 05:02