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 @@ -276,6 +276,7 @@ Kojo Idrissa
Kostis Anagnostopoulos
Kristoffer Nordström
Kyle Altendorf
Kyue
Lawrence Mitchell
Lee Kamentsky
Leonardus Chen
Expand Down
1 change: 1 addition & 0 deletions changelog/14775.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed an internal ``AssertionError`` that could occur when a fixture's setup fails before its underlying function is even called (e.g. when the class-scoped-fixture-as-instance-method deprecation warning is turned into an error via ``-W error``). The failure is now cached like any other setup error instead of leaking finalizer state into the next test.
25 changes: 13 additions & 12 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,21 +1320,22 @@ def pytest_fixture_setup(
for argname in fixturedef.argnames:
kwargs[argname] = request.getfixturevalue(argname)

fixturefunc = resolve_fixture_function(fixturedef, request)
my_cache_key = fixturedef.cache_key(request)

if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
fixturefunc
):
auto_str = " with autouse=True" if fixturedef._autouse else ""
fail(
f"{request.node.name!r} requested an async fixture {request.fixturename!r}{auto_str}, "
"with no plugin or hook that handled it. This is an error, as pytest does not natively support it.\n"
"See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture",
pytrace=False,
)

try:
fixturefunc = resolve_fixture_function(fixturedef, request)

if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
fixturefunc
):
auto_str = " with autouse=True" if fixturedef._autouse else ""
fail(
f"{request.node.name!r} requested an async fixture {request.fixturename!r}{auto_str}, "
"with no plugin or hook that handled it. This is an error, as pytest does not natively support it.\n"
"See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture",
pytrace=False,
)

result = call_fixture_func(fixturefunc, request, kwargs)
except TEST_OUTCOME as e:
if isinstance(e, skip.Exception):
Expand Down
36 changes: 36 additions & 0 deletions testing/python/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -3801,6 +3801,42 @@ def test_3(bad): pass
lines2 = failures[2].longrepr.reprtraceback.reprentries[0].lines
assert len(lines1) == len(lines2)

def test_exception_before_fixture_func_called_is_cached(
self, pytester: Pytester
) -> None:
"""A failure that happens while resolving the fixture function itself
(e.g. a deprecation warning promoted to an error by ``-W error``, before
the fixture body ever runs) must be cached on the FixtureDef like any
other setup failure. Otherwise leftover finalizer state leaks into the
next test using the same class-scoped fixture and crashes pytest
internals instead of reporting the (repeated) original error (#14775).
"""
pytester.makepyfile(
"""
import pytest

class TestFixt:
@pytest.fixture(scope="class")
def fixt(self):
yield

def test_1(self, fixt):
pass

def test_2(self, fixt):
pass
"""
)
result = pytester.runpytest("-Werror")
result.assert_outcomes(errors=2)
assert "AssertionError" not in result.stdout.str()
result.stdout.fnmatch_lines(
[
"*PytestRemovedIn10Warning: Class-scoped fixtures defined as "
"instance methods are deprecated.*",
]
)


class TestShowFixtures:
def test_funcarg_compat(self, pytester: Pytester) -> None:
Expand Down