我试图避免在测试中重复过多的样板,而我想以更结构化的方式重写它们。假设我有两个不同的解析器,都可以将文本解析为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."
现在的问题是,如何创建一个看起来像这样的doc()
灯具:
@pytest.fixture(params=???)
def doc(parser, text):
return parser.parse(text)
其中parser
的参数设置为parser_a和parser_b,而text
的参数设置为short_text和long_text。这意味着总共doc
将总共测试解析器和文本的四种组合。
关于PyTest的参数化灯具的文档非常模糊,我无法找到解决方法。欢迎所有帮助。
答案 0 :(得分:1)
您的灯具应如下所示:
enum
并按以下方式使用它:
@pytest.fixture(scope='function')
def doc_fixture(request):
parser = request.param[0]
text = request.param[1]
return parser.parse(text)
混合和匹配参数组合
这是另一个提供不同参数组合的示例:
@pytest.mark.parametrize('doc_fixture', [parser_1, 'short text'], indirect=True)
def test_sth(doc_fixture):
... # Perform tests
一个示例运行结果(预期失败的测试):
from argparse import Namespace
import pytest
@pytest.fixture(scope='function')
def doc_fixture(request):
first_arg, second_arg = request.param
s = Namespace()
s.one = first_arg
s.two = second_arg
return s
@pytest.mark.parametrize(
'doc_fixture',
[
('parserA', 'ShortText'),
('parserA', 'LongText'),
('parserB', 'ShortText'),
('parserB', 'LongText')
],
indirect=True
)
def test_something(doc_fixture):
assert doc_fixture == ''
答案 1 :(得分: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)
所得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 ==============================
更新: 我为解析器添加了一个单例工厂,如注释中作为示例所讨论的。
注意:
我尝试按照@hoefling的建议使用pytest.lazy_fixture
。这行得通,并且可以直接从固定装置传递解析器和文本,但是我无法(现在)以仅将每个解析器实例化一次的方式使其工作。作为参考,以下是使用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)