首页 > 解决方案 > 如何编写分两个阶段运行的pytest测试,每个阶段按顺序运行?

问题描述

我在 pytest 中编写了一堆测试,其中许多采用以下形式(伪代码):

def test1(self, ...):
    expected_events = do_thing1_that_should_generate_events(...)
    assert wait_for_events_to_appear_in_ui(expected_events)

事件需要几分钟才能出现在 UI 中,因此会在 UI 中wait_for_events_to_appear_in_ui轮询它们,并设置超时。这行得通,但是每个这样的测试都会运行几分钟,所以总运行时间会很长。

所以,我想先运行所有的do_thingX动作,然后运行所有的验证。我考虑将预期的事件添加到某个列表中,并进行最终测试来验证所有这些事件 - 但这意味着如果do_thing1,pytest 不会报告test1失败,而是最终测试 - 这是次优的。

理想情况下,测试看起来像:

def test1(self, ...):
    expected_events = do_thing1_that_should_generate_events(...)
    # some kind of magic that works like yield?
    assert wait_for_events_to_appear_in_ui(expected_events)

我怎样才能做到这一点?

标签: pythonpytest

解决方案


首先看一下pytest-xdist,它用于在多个进程之间进行并行测试等。不过,我不知道这对您的情况有多好。请继续阅读以获取更具体的解决方案。


您需要在问题中暗示的单个测试函数中管理这些测试的异步运行。也许你使用同步代码,也许你使用异步框架;这与此正交。我将假设您可以自己管理它,并且无论如何它都不是特定于 pytest 的东西。

要解决一个 pytest 测试函数中一揽子通过/失败的问题,您需要子测试。pytest-subtestssubtests允许您通过提供一个夹具(它也支持subTest()from )在单个测试函数中编写子测试unittest

这是提供的示例:

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

pytest的输出:

λ pytest .tmp\test-subtest.py
======================== test session starts ========================
...
collected 1 item

.tmp\test-subtest.py .F.F..                                    [100%]

============================= FAILURES ==============================
____________________ test [custom message] (i=1) ____________________

    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

.tmp\test-subtest.py:4: AssertionError
____________________ test [custom message] (i=3) ____________________

    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

.tmp\test-subtest.py:4: AssertionError
================ 2 failed, 1 passed in 0.07 seconds =================

乍一看,失败/通过的数字有点违反直觉。看起来我们有五个子测试,其中两个应该失败;事实上,我们报告了两次失败。但是我们只有一个通过测试,而不是三个。那一关就是test_func()它自己;由于该with块正在捕获内部的断言,test_func()因此不会失败。其他三个子测试没有失败,因此根本没有报告——尽管我们确实看到它们出现在进度点中。

总而言之:自己管理测试,并使用pytest-subtests获取单独的失败报告。


推荐阅读