如何使用子测试

9.0 版本新增。

注意

此功能尚处于实验阶段。其行为,特别是报告失败的方式,可能会在未来版本中有所演变。然而,其核心功能和用法被认为是稳定的。

pytest 允许在正常测试中分组断言,这被称为 子测试

子测试是参数化的替代方案,特别是在收集时不知道确切的参数化值时非常有用。

# content of test_subtest.py


def test(subtests):
    for i in range(5):
        with subtests.test(msg="custom message", i=i):
            assert i % 2 == 0

每个断言失败或错误都会被上下文管理器捕获并单独报告。

$ pytest -q test_subtest.py
uuuuuF                                                               [100%]
================================= FAILURES =================================
_______________________ test [custom message] (i=1) ________________________

subtests = <_pytest.subtests.Subtests object at 0xdeadbeef0001>

    def test(subtests):
        for i in range(5):
            with subtests.test(msg="custom message", i=i):
>               assert i % 2 == 0
E               assert (1 % 2) == 0

test_subtest.py:6: AssertionError
_______________________ test [custom message] (i=3) ________________________

subtests = <_pytest.subtests.Subtests object at 0xdeadbeef0001>

    def test(subtests):
        for i in range(5):
            with subtests.test(msg="custom message", i=i):
>               assert i % 2 == 0
E               assert (3 % 2) == 0

test_subtest.py:6: AssertionError
___________________________________ test ___________________________________
contains 2 failed subtests
========================= short test summary info ==========================
SUBFAILED[custom message] (i=1) test_subtest.py::test - assert (1 % 2) == 0
SUBFAILED[custom message] (i=3) test_subtest.py::test - assert (3 % 2) == 0
FAILED test_subtest.py::test - contains 2 failed subtests
3 failed, 3 subtests passed in 0.12s

在上面的输出中

  • 子测试失败被报告为 SUBFAILED

  • 子测试首先被报告,“顶层”测试最后单独报告。

请注意,可以在同一个测试中多次使用 subtests,甚至可以与 subtests.test 块外部的正常断言混合使用。

def test(subtests):
    for i in range(5):
        with subtests.test("stage 1", i=i):
            assert i % 2 == 0

    assert func() == 10

    for i in range(10, 20):
        with subtests.test("stage 2", i=i):
            assert i % 2 == 0

注意

有关子测试的替代方案,请参阅 如何参数化夹具和测试函数

详细程度

默认情况下,只显示 子测试失败。更高的详细程度 (-v) 也会显示 通过 的子测试的进度输出。

可以通过设置 verbosity_subtests 来控制子测试的详细程度。

类型

pytest.Subtests 已导出,因此可以在类型注解中使用。

def test(subtests: pytest.Subtests) -> None: ...

参数化与子测试

虽然 传统的 pytest 参数化subtests 相似,但它们有重要的区别和用例。

参数化

  • 发生在收集时。

  • 生成单独的测试。

  • 可以通过命令行引用参数化测试。

  • 与处理测试执行的插件(例如 --last-failed)配合良好。

  • 非常适合决策表测试。

子测试

  • 发生在测试执行期间。

  • 在收集时未知。

  • 可以动态生成。

  • 无法通过命令行单独引用。

  • 处理测试执行的插件无法针对单个子测试。

  • 子测试内的断言失败不会中断测试,让用户可以在同一个报告中看到所有失败。

注意

此功能最初作为单独的插件在 pytest-subtests 中实现,但自 9.0 起已合并到核心中。

核心实现应与插件实现兼容,只是它不包含控制子测试输出的自定义命令行选项。