Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ Jeff Widman
Jenni Rinker
Jens Tröger
Jiajun Xu
Jinhyuk Sung
John Eddie Ayson
John Litborn
John Towler
Expand Down
1 change: 1 addition & 0 deletions changelog/14575.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``--last-failed`` now reruns ``unittest.TestCase`` tests that contain failed subtests.
15 changes: 14 additions & 1 deletion src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from _pytest.nodes import Directory
from _pytest.nodes import File
from _pytest.reports import TestReport
from _pytest.subtests import SubtestReport


CACHEDIR_FILES: dict[str, bytes] = {
Expand Down Expand Up @@ -319,6 +320,7 @@ def __init__(self, config: Config) -> None:
self.active = any(config.getoption(key) for key in active_keys)
assert config.cache
self.lastfailed: dict[str, bool] = config.cache.get("cache/lastfailed", {})
self._tests_with_failed_subtests: set[str] = set()
self._previously_failed_count: int | None = None
self._report_status: str | None = None
self._skipped_files = 0 # count skipped files during collection due to --lf
Expand Down Expand Up @@ -346,7 +348,18 @@ def pytest_report_collectionfinish(self) -> str | None:
return None

def pytest_runtest_logreport(self, report: TestReport) -> None:
if (report.when == "call" and report.passed) or report.skipped:
if report.when == "setup":
self._tests_with_failed_subtests.discard(report.nodeid)

if isinstance(report, SubtestReport):
if report.failed:
self._tests_with_failed_subtests.add(report.nodeid)
self.lastfailed[report.nodeid] = True
return

if (
(report.when == "call" and report.passed) or report.skipped
) and report.nodeid not in self._tests_with_failed_subtests:
self.lastfailed.pop(report.nodeid, None)
elif report.failed:
self.lastfailed[report.nodeid] = True
Expand Down
28 changes: 28 additions & 0 deletions testing/test_subtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,34 @@ def test_zaz(self):
]
)

def test_last_failed(
self, pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Check that --last-failed reruns tests with failed unittest subtests."""
monkeypatch.setenv("COLUMNS", "120")
pytester.makepyfile(
"""
from unittest import TestCase

class T(TestCase):
def test_foo(self):
for i in range(3):
with self.subTest(index=i):
assert i % 2 == 0
"""
)
result = pytester.runpytest("-v")
result.stdout.fnmatch_lines(["* 1 failed, 1 passed, 2 subtests passed in *"])

result = pytester.runpytest("-v", "--last-failed")
result.stdout.fnmatch_lines(
[
"*collected 1 item",
"run-last-failure: rerun previous 1 failure",
"* 1 failed, 1 passed, 2 subtests passed in *",
]
)

def test_passes(
self, pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down
Loading