Skip to content
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ Simon Holesch
Simon Kerr
Skylar Downes
Srinivas Reddy Thatiparthy
Stamatis-Christos Saridakis
Stefaan Lippens
Stefan Farmbauer
Stefan Scherfke
Expand Down
2 changes: 2 additions & 0 deletions changelog/13169.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
When using ``--setup-show`` or ``--setup-only``, show a ``SETUP START``
line before each fixture starts executing, so a stalled fixture setup can be identified.
11 changes: 9 additions & 2 deletions src/_pytest/setuponly.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ def pytest_addoption(parser: Parser) -> None:
def pytest_fixture_setup(
fixturedef: FixtureDef[object], request: SubRequest
) -> Generator[None, object, object]:
if request.config.option.setupshow:
# Report this fixture before entering user code, including if it hangs.
_show_fixture_action(fixturedef, request.config, "SETUP START", end_line=True)
try:
return (yield)
finally:
Expand Down Expand Up @@ -62,7 +65,7 @@ def pytest_fixture_post_finalizer(


def _show_fixture_action(
fixturedef: FixtureDef[object], config: Config, msg: str
fixturedef: FixtureDef[object], config: Config, msg: str, *, end_line: bool = False
) -> None:
capman = config.pluginmanager.getplugin("capturemanager")
if capman:
Expand All @@ -85,7 +88,11 @@ def _show_fixture_action(
if hasattr(fixturedef, "cached_param"):
tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]")

tw.flush()
# End the early marker before any output from the fixture body.
if end_line:
tw.line()
else:
tw.flush()

if capman:
capman.resume_global_capture()
Expand Down
4 changes: 4 additions & 0 deletions testing/python/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -4626,10 +4626,14 @@ def test_second(my_fixture):
result.stdout.fnmatch_lines(
[
"test_fixtures.py::test_first ",
" SETUP START F my_fixture",
"",
" SETUP F my_fixture",
" test_fixtures.py::test_first (fixtures used: my_fixture, request) PASSED",
"test_fixtures.py::test_first ERROR",
"test_fixtures.py::test_second ",
" SETUP START F my_fixture",
"",
" SETUP F my_fixture",
" test_fixtures.py::test_second (fixtures used: my_fixture, request) PASSED",
" TEARDOWN F my_fixture",
Expand Down
69 changes: 69 additions & 0 deletions testing/test_setuponly.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,75 @@
import pytest


@pytest.mark.parametrize("mode", ["--setup-show", "--setup-only"])
def test_setup_show_announces_fixture_before_body(
pytester: Pytester, mode: str
) -> None:
p = pytester.makepyfile(
"""
import pytest

@pytest.fixture(params=["example"], ids=["sample"])
def slow():
print("BODY-ENTERED", flush=True)
yield "value"

def test_value(slow):
assert slow == "value"
"""
)

result = pytester.runpytest(mode, "-s", p)
assert result.ret == 0
output = result.stdout.str()
assert "SETUP START F slow" in output
assert "SETUP F slow['sample']" in output
assert "TEARDOWN F slow['sample']" in output
assert output.index("SETUP START F slow") < output.index("BODY-ENTERED")
assert output.index("BODY-ENTERED") < output.index("SETUP F slow")
assert "SETUP START F slow\n" in output


def test_setup_show_announces_failed_fixture(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest

@pytest.fixture
def broken():
raise ValueError("fixture setup failed")

def test_failure(broken):
pass
"""
)

result = pytester.runpytest("--setup-show", p)
assert result.ret == 1
output = result.stdout.str()
assert "SETUP START F broken" in output
assert "SETUP F broken" in output
assert output.index("SETUP START F broken") < output.index("SETUP F broken")


def test_no_setup_start_when_not_requested(pytester: Pytester) -> None:
p = pytester.makepyfile(
"""
import pytest

@pytest.fixture
def normal():
return 1

def test_normal(normal):
assert normal == 1
"""
)
result = pytester.runpytest(p)
assert result.ret == 0
assert "SETUP START" not in result.stdout.str()


@pytest.fixture(params=["--setup-only", "--setup-plan", "--setup-show"], scope="module")
def mode(request):
return request.param
Expand Down
Loading