pytest:将测试标记为“必须通过”,如果失败则停止测试。

3

我正在尝试实现一个名为@pytest.mark.must_pass的新的pytest标记,用于指示如果标记的测试失败,pytest应该跳过所有后续测试并终止。

我已经使用pytest_runtest_call钩子使pytest在标记的测试失败时终止,但我使用的是pytest.exit,它不会打印回溯,也不会显示有关问题测试的失败指示。

我需要这个失败出现像任何其他测试失败一样,除了pytest在打印出它需要打印的内容以详细说明失败后停止测试。

到目前为止我的代码如下:

# Copied this implementation from _pytest.runner
def pytest_runtest_call(item):
    _update_current_test_var(item, "call")
    try:
        del sys.last_type
        del sys.last_value
        del sys.last_traceback
    except AttributeError:
        pass
    try:
        item.runtest()
    except Exception:
        # Store trace info to allow postmortem debugging
        type, value, tb = sys.exc_info()
        assert tb is not None
        tb = tb.tb_next  # Skip *this* frame
        sys.last_type = type
        sys.last_value = value
        sys.last_traceback = tb
        del type, value, tb  # Get rid of these in this frame

        # If test is marked as must pass, stop testing here
        if item.iter_markers(name = "must_pass"):
            pytest.exit('Test marked as "must_pass" failed, terminating.')

        raise

pytest中是否已经有用于完成此操作的机制?

非常感谢您的任何帮助。

谢谢。


根据您提供的代码,pytest将在到达must_pass标记后总是退出。 - gold_cy
@aws_apprentice 是的,但它不会显示为失败的测试。例如,它不会显示追溯或以红色打印“FAILED”。 - Yoel Einhoren
那么,如果遇到这个标记,所期望的行为是什么?是停止运行任何测试并立即停止吗? - gold_cy
是的,当一个被标记为“必须通过”的测试失败时,它应该像其他测试一样显示失败,并跳过所有剩余的测试,然后退出。 - Yoel Einhoren
2个回答

3

可以通过使用pytest_runtest_makereportpytest_runtest_setup实现。

在你的conftest.py文件中,你应该放置以下内容:

import pytest


def pytest_runtest_makereport(item, call):
    if item.iter_markers(name='must_pass'):
        if call.excinfo is not None:
            parent = item.parent
            parent._mpfailed = item


def pytest_runtest_setup(item):
    must_pass_failed = getattr(item.parent, '_mpfailed', None)
    if must_pass_failed is not None:
        pytest.skip('must pass test failed (%s)' % must_pass_failed.name)

现在我们用以下内容进行测试:

import pytest


def foo(a, b):
    return a + b


def test_foo_1():
    assert foo(1, 1) == 2


@pytest.mark.must_pass
def test_foo_2():
    assert foo(2, 2) == 6


def test_foo_3():
    assert foo(3, 3) == 6


def test_foo_4():
    assert foo(4, 4) == 8

我们期望看到预期输出:
     ▲ = pytest test.py 
=============================================================== test session starts ================================================================
platform darwin -- Python 3.6.5, pytest-4.6.2, py-1.8.0, pluggy-0.12.0
rootdir: /Users/foo/Desktop/testing, inifile: pytest.ini
plugins: cov-2.7.1
collected 4 items                                                                                                                                  

test.py .Fss                                                                                                                                 [100%]

===================================================================== FAILURES =====================================================================
____________________________________________________________________ test_foo_2 ____________________________________________________________________

    @pytest.mark.must_pass
    def test_foo_2():
>       assert foo(2, 2) == 6
E       assert 4 == 6
E        +  where 4 = foo(2, 2)

test.py:14: AssertionError
================================================== 1 failed, 1 passed, 2 skipped in 0.08 seconds ===================================================

1
谢谢,这正是我所需要的 :) - Yoel Einhoren
如果我有多个文件和多个测试,如何使用相同的测试通行证? - VikasKM
@VikasKM 这取决于您自己的决定,这里的答案并不是针对这样的问题。您已经拥有了基础,可以构建适合您需求的解决方案。 - gold_cy
@gold_cy 我使用了依赖模块并解决了它。 - VikasKM
我会在pytest_runtest_makereport中添加 "if item.iter_markers(name='skip'): return" 的代码,这样它就不会期望跳过的测试通过了。 - Jake OPJ

0
将代码粘贴到被接受的答案中会产生以下结果:
'must_pass' not found in `markers` configuration option

对于想要使用而不是实现这个的任何人来说,可以使用 pytest-dependency 来实现相同的功能:

  1. pip 安装 pytest-dependency
  2. 查看 this answer 以获取使用方法。

2
请注意,不需要为此添加第三方库,只需按照这里所示,在您的pytest.ini中注册标记即可。 - gold_cy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接