Skip to content

Commit 9cd14b4

Browse files
committed
doctest: fix autouse fixtures possibly not getting picked up
Fix #11929. Figured out what's going on. We have the following collection tree: ``` <Dir pyspacewar> <Dir src> <Package pyspacewar> <Package tests> <DoctestModule test_main.py> <DoctestItem pyspacewar.tests.test_main.doctest_main> ``` And the `test_main.py` contains an autouse fixture (`fake_game_ui`) that `doctest_main` needs in order to run properly. The fixture doesn't run! It doesn't run because nothing collects the fixtures from (calls `parsefactories()` on) the `test_main.py` `DoctestModule`. How come it only started happening with commit ab63ebb? Turns out it mostly only worked accidentally. Each `DoctestModule` is also collected as a normal `Module`, with the `Module` collected after the `DoctestModule`. For example, if we add a non-doctest test to `test_main.py`, the collection tree looks like this: ``` <Dir pyspacewar> <Dir src> <Package pyspacewar> <Package tests> <DoctestModule test_main.py> <DoctestItem pyspacewar.tests.test_main.doctest_main> <Module test_main.py> <Function test_it> ``` Now, `Module` *does* collect fixtures. When autouse fixtures are collected, they are added to the `_nodeid_autousenames` dict. Before ab63ebb, `DoctestItem` consults `_nodeid_autousenames` at *setup* time. At this point, the `Module` has collected and so it ended up picking the autouse fixture (this relies on another "accident", that the `DoctestModule` and `Module` have the same node ID). After ab63ebb, `DoctestItem` consults `_nodeid_autousenames` at *collection* time (= when it's created). At this point, the `Module` hasn't collected yet, so the autouse fixture is not picked out. The fix is simple -- have `DoctestModule.collect()` call `parsefactories`. From some testing I've done it shouldn't have negative consequences (I hope).
1 parent 6e5008f commit 9cd14b4

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

changelog/11929.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a regression in pytest 8.0.0 whereby autouse fixtures defined in a module get ignored by the doctests in the module.

src/_pytest/doctest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,10 @@ def _from_module(self, module, object):
567567
else:
568568
raise
569569

570+
# While doctests currently don't support fixtures directly, we still
571+
# need to pick up autouse fixtures.
572+
self.session._fixturemanager.parsefactories(self)
573+
570574
# Uses internal doctest module parsing mechanism.
571575
finder = MockAwareDocTestFinder()
572576
optionflags = get_optionflags(self.config)

testing/test_doctest.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,38 @@ def auto(request):
13761376
str(result.stdout.no_fnmatch_line("*FAILURES*"))
13771377
result.stdout.fnmatch_lines(["*=== 1 passed in *"])
13781378

1379+
@pytest.mark.parametrize("scope", [*SCOPES, "package"])
1380+
def test_auto_use_defined_in_same_module(
1381+
self, pytester: Pytester, scope: str
1382+
) -> None:
1383+
"""Autouse fixtures defined in the same module as the doctest get picked
1384+
up properly.
1385+
1386+
Regression test for #11929.
1387+
"""
1388+
pytester.makepyfile(
1389+
f"""
1390+
import pytest
1391+
1392+
AUTO = "the fixture did not run"
1393+
1394+
@pytest.fixture(autouse=True, scope="{scope}")
1395+
def auto(request):
1396+
global AUTO
1397+
AUTO = "the fixture ran"
1398+
1399+
def my_doctest():
1400+
'''My doctest.
1401+
1402+
>>> my_doctest()
1403+
'the fixture ran'
1404+
'''
1405+
return AUTO
1406+
"""
1407+
)
1408+
result = pytester.runpytest("--doctest-modules")
1409+
result.assert_outcomes(passed=1)
1410+
13791411

13801412
class TestDoctestNamespaceFixture:
13811413
SCOPES = ["module", "session", "class", "function"]

0 commit comments

Comments
 (0)