如何将参数化夹具作为参数传递给另一个夹具

Bra*_*roy 6 python fixtures pytest parameterized-unit-test

我试图避免在测试中重复太多样板文件,并且我想以更结构化的方式重写它们。假设我有两个不同的解析器,它们都可以将文本解析为doc. 该文档随后将用于其他测试。最终目标是公开一个doc()可在其他测试中使用的固定装置,并且以运行给定解析器和文本的所有组合的方式进行参数化。

@pytest.fixture
def parser_a():
    return "parser_a"  # actually a parser object

@pytest.fixture
def parser_b():
    return "parser_b"  # actually a parser object

@pytest.fixture
def short_text():
    return "Lorem ipsum"

@pytest.fixture
def long_text():
    return "If I only knew how to bake cookies I could make everyone happy."
Run Code Online (Sandbox Code Playgroud)

现在的问题是,如何创建一个doc()如下所示的装置:

@pytest.fixture(params=???)
def doc(parser, text):
    return parser.parse(text)
Run Code Online (Sandbox Code Playgroud)

其中parser参数化为parser_a和parser_b,以及textshort_text和long_text。这意味着总共doc将测试解析器和文本的四种组合。

PyTest 参数化装置的文档非常模糊,我找不到如何解决这个问题的答案。欢迎大家帮忙。

MrB*_*men 1

不确定这是否正是您所需要的,但您可以使用函数而不是固定装置,并将它们组合在固定装置中:

import pytest

class Parser:  # dummy parser for testing
    def __init__(self, name):
        self.name = name

    def parse(self, text):
        return f'{self.name}({text})'


class ParserFactory:  # do not recreate existing parsers
    parsers = {}

    @classmethod
    def instance(cls, name):
        if name not in cls.parsers:
            cls.parsers[name] = Parser(name)
        return cls.parsers[name]

def parser_a():
    return ParserFactory.instance("parser_a") 

def parser_b():
    return ParserFactory.instance("parser_b")

def short_text():
    return "Lorem ipsum"

def long_text():
    return "If I only knew how to bake cookies I could make everyone happy."


@pytest.fixture(params=[long_text, short_text])
def text(request):
    yield request.param

@pytest.fixture(params=[parser_a, parser_b])
def parser(request):
    yield request.param

@pytest.fixture
def doc(parser, text):
    yield parser().parse(text())

def test_doc(doc):
    print(doc)
Run Code Online (Sandbox Code Playgroud)

pytest 的输出结果是:

============================= test session starts =============================
...
collecting ... collected 4 items

test_combine_fixt.py::test_doc[parser_a-long_text] PASSED                [ 25%]parser_a(If I only knew how to bake cookies I could make everyone happy.)

test_combine_fixt.py::test_doc[parser_a-short_text] PASSED               [ 50%]parser_a(Lorem ipsum)

test_combine_fixt.py::test_doc[parser_b-long_text] PASSED                [ 75%]parser_b(If I only knew how to bake cookies I could make everyone happy.)

test_combine_fixt.py::test_doc[parser_b-short_text] PASSED               [100%]parser_b(Lorem ipsum)


============================== 4 passed in 0.05s ==============================
Run Code Online (Sandbox Code Playgroud)

更新: 我为解析器添加了一个单例工厂,如评论中所讨论的作为示例。

注意:pytest.lazy_fixture我尝试按照@hoefling的建议 使用。这是可行的,并且可以直接从固定装置传递解析器和文本,但我(还)无法以每个解析器仅实例化一次的方式工作。作为参考,如果使用以下代码,则更改后的代码如下pytest.lazy_fixture

@pytest.fixture
def parser_a():
    return Parser("parser_a")

@pytest.fixture
def parser_b():
    return Parser("parser_b")

@pytest.fixture
def short_text():
    return "Lorem ipsum"

@pytest.fixture
def long_text():
    return "If I only knew how to bake cookies I could make everyone happy."


@pytest.fixture(params=[pytest.lazy_fixture('long_text'),
                        pytest.lazy_fixture('short_text')])
def text(request):
    yield request.param

@pytest.fixture(params=[pytest.lazy_fixture('parser_a'),
                        pytest.lazy_fixture('parser_b')])
def parser(request):
    yield request.param


@pytest.fixture
def doc(parser, text):
    yield parser.parse(text)


def test_doc(doc):
    print(doc)
Run Code Online (Sandbox Code Playgroud)