我有很多使用随机生成的数据作为输入的测试。为了确保失败是可重复的,我将它们组织如下:
import random
import pytest
@pytest.fixture
def seed():
return random.getrandbits(32)
def test_foo(seed):
# seed the random number generators, and generate random data
# test foo
def test_bar(seed):
# seed the random number generators, and generate random data
# test bar
def test_baz(seed):
# seed the random number generators, and generate random data
# test baz
这样,如果发生故障,pytest 会报告
seed
的值,我可以临时更改 seed()
固定装置以返回该特定值,修复故障,然后将其恢复。 (可能有更优雅的方法来做到这一点。)
上述结构的一个问题是,每次测试中都会重复大量代码来生成随机数据。
因此,我决定将测试模块重构为:
import random
import pytest
@pytest.fixture
def seed():
return random.getrandbits(32)
@pytest.fixture
def stimulus(seed):
# seed the random number generators and generate random data
return data
def test_foo(stimulus):
# test foo
def test_bar(stimulus):
# test bar
def test_baz(stimulus):
# test baz
现在测试仍然是可重复的,因为如果
seed()
夹具返回(或重写返回)相同的值,我们会得到相同的测试结果。但是,如果失败,pytest 会打印出 stimulus
的值,该值通常太长而无法显示在屏幕上。 (另外,更改 stimulus()
以返回完全相同的数据会更加困难。)
有没有办法告诉 pytest“如果失败,打印出最后一个固定装置的值,即
seed
而不是第一个固定装置 stimulus
”?
您可以做的一件事(如果您也可以看到
stimulus
的输出)是让 stimulus
夹具返回 (seed, data)
(并相应地调整您的测试函数)并在 pytest 命令中包含开关--showlocals
。这样,您将看到夹具的值,只是现在它也显示了 seed
的值..
测试后使用产量夹具清理:
import base64, random, secrets, pytest
@pytest.fixture()
def seed(request):
seed = secrets.randbits(32)
yield seed
if request.session.testsfailed:
request.session.warn(UserWarning(f"{request.node.name} seed was {seed}"))
@pytest.fixture()
def stimulus(seed: int):
return random.Random(seed).randbytes(12)
def test_pass(stimulus):
assert len(base64.b64encode(stimulus)) == 16
def test_fail(stimulus):
assert len(base64.b64encode(stimulus)) == 12
上面的示例使用警告将种子提供给开发人员。输出看起来像这样:
tests/so.py::test_fail
overflow:0: UserWarning: test_fail seed was 3753932574
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html