Reputation: 41
I am trying to implement a new pytest
marker, called @pytest.mark.must_pass
, to indicate that if the marked test fails, pytest should skip all subsequent tests and terminate.
I have been able to use the pytest_runtest_call
hook to get pytest to terminate if the marked test failed, but I am using pytest.exit
, which does not print a traceback, nor does it show the failure indication for the test in question.
I need this failure to appear as any other test failure, except that pytest stops testing after it prints whatever it needs to print to detail the failure.
My code so far:
# 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
Is there already a mechanism for doing this built into pytest?
Any help will be greatly appreciated.
Thanks.
Upvotes: 3
Views: 3141
Reputation: 6018
Pasting the code in the accepted answer yields this:
'must_pass' not found in `markers` configuration option
For anyone coming here wanting to use – not implement – this, the same can be achieved with pytest-dependency:
Upvotes: 0
Reputation: 14216
So this can be achieved by using pytest_runtest_makereport
and pytest_runtest_setup
In your conftest.py
you would place the following:
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)
And now when we test it with the following:
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
We see the desired output:
▲ = 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 ===================================================
Upvotes: 5