pytest 8.2.2 breaks pytest-rerunfailures for tests that inherit unittest.TestCase
mdmintz opened this issue · 4 comments
pytest 8.2.2 breaks pytest-rerunfailures for tests that inherit unittest.TestCase
The code: (placed in new_file.py
)
from unittest import TestCase
class MyTestClass(TestCase):
def test_base(self):
self.fail()
Run command:
pytest new_file.py --reruns=1
Expected output: (Using pytest==8.2.1
/ previous version)
self = <examples.new_file.MyTestClass testMethod=test_base>
def test_base(self):
> self.fail()
E AssertionError: None
new_file.py:5: AssertionError
Regression output: (Using pytest==8.2.2
/ latest version)
self = <TestCaseFunction test_base>
def runtest(self) -> None:
from _pytest.debugging import maybe_wrap_pytest_function_for_tracing
testcase = self.instance
> assert testcase is not None
E AssertionError
The error occurs during the rerun
of the test.
The failure line should be self.fail()
. Instead, it's assert testcase is not None
.
This causes the rerun
to never happen. (The test only ran the first time)
Tested on a Mac. Here's the pip list
:
Package Version
-------------------- -------
iniconfig 2.0.0
packaging 24.0
pip 24.0
pluggy 1.5.0
pytest 8.2.2
pytest-rerunfailures 14.0
Thank you for filing this with succinct replication steps. I spent hours yesterday trying to figure out why our CI/CD was failing
We can probably accommodate rerunfailures here. I will take a look.
I think switching pytest_runtest_makereport to the "new style" hook will make the issue go away
BEFORE
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
result = outcome.get_result()
if result.when == "setup":
# clean failed statuses at the beginning of each test/rerun
setattr(item, "_test_failed_statuses", {})
# create a dict to store error-check results for each stage
setattr(item, "_terminal_errors", {})
_test_failed_statuses = getattr(item, "_test_failed_statuses", {})
_test_failed_statuses[result.when] = result.failed
item._test_failed_statuses = _test_failed_statuses
item._terminal_errors[result.when] = _should_hard_fail_on_error(item, result)
AFTER
@pytest.hookimpl(wrapper=True)
def pytest_runtest_makereport(item, call):
result = yield
if result.when == "setup":
# clean failed statuses at the beginning of each test/rerun
setattr(item, "_test_failed_statuses", {})
# create a dict to store error-check results for each stage
setattr(item, "_terminal_errors", {})
_test_failed_statuses = getattr(item, "_test_failed_statuses", {})
_test_failed_statuses[result.when] = result.failed
item._test_failed_statuses = _test_failed_statuses
item._terminal_errors[result.when] = _should_hard_fail_on_error(item, result)
return result
I think switching pytest_runtest_makereport to the "new style" hook will make the issue go away
BEFORE
@pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield result = outcome.get_result() if result.when == "setup": # clean failed statuses at the beginning of each test/rerun setattr(item, "_test_failed_statuses", {}) # create a dict to store error-check results for each stage setattr(item, "_terminal_errors", {}) _test_failed_statuses = getattr(item, "_test_failed_statuses", {}) _test_failed_statuses[result.when] = result.failed item._test_failed_statuses = _test_failed_statuses item._terminal_errors[result.when] = _should_hard_fail_on_error(item, result)AFTER
@pytest.hookimpl(wrapper=True) def pytest_runtest_makereport(item, call): result = yield if result.when == "setup": # clean failed statuses at the beginning of each test/rerun setattr(item, "_test_failed_statuses", {}) # create a dict to store error-check results for each stage setattr(item, "_terminal_errors", {}) _test_failed_statuses = getattr(item, "_test_failed_statuses", {}) _test_failed_statuses[result.when] = result.failed item._test_failed_statuses = _test_failed_statuses item._terminal_errors[result.when] = _should_hard_fail_on_error(item, result) return result
disregard, the issue is still there :^(