Skip to content

Fix phantom nodeid for tests collected outside rootpath (#13254)#14417

Open
mnajaf wants to merge 1 commit intopytest-dev:mainfrom
mnajaf:fix-13254-nodeid-outside-rootpath
Open

Fix phantom nodeid for tests collected outside rootpath (#13254)#14417
mnajaf wants to merge 1 commit intopytest-dev:mainfrom
mnajaf:fix-13254-nodeid-outside-rootpath

Conversation

@mnajaf
Copy link
Copy Markdown

@mnajaf mnajaf commented Apr 24, 2026

Fixes #13254.

Problem

When pytest collects tests from a path that lives outside rootpath
(for example a pytest.ini inside tests/a/aa/ while an additional
test file sits at tests/b/bb/test_b.py), the displayed node ID for
the out-of-rootpath test is a synthesized path that does not exist on
disk.

Minimal reproducer:

tests/
├── a/aa/pytest.ini
├── a/aa/test_a.py
└── b/bb/test_b.py

Run from tests/a/:

pytest ./aa ../b/bb -vv

Before this change:

aa\test_a.py::test_a PASSED
aa\test_b.py::test_b <- ..\..\b\bb\test_b.py PASSED

aa\test_b.py does not exist; the file is actually at ../../b/bb/test_b.py.

Cause

Node.__init__ in src/_pytest/nodes.py falls back to
_check_initialpaths_for_relpath when the collected path cannot be
expressed relative to rootpath. That yields a bare node ID like
test_b.py::test_b. Config.cwd_relative_nodeid then assumed every
node ID was rootpath-relative and rebuilt the display string as
rootpath / base_path_part, which in this case points nowhere.

Fix

Check whether the synthesized fullpath actually exists on disk. If
it does not, the node ID was resolved via the initial paths (not
rootpath), so return it unchanged. The <- ... location hint rendered
by the terminal reporter continues to show the real file location, so
the correct path information is still presented without the misleading
prefix.

After this change the same invocation produces:

aa\test_a.py::test_a PASSED
test_b.py::test_b <- ..\..\b\bb\test_b.py PASSED

Tests

Added TestNodeIDHandling::test_nodeid_outside_rootpath_not_synthesized
in testing/test_terminal.py. The test reproduces the issue layout
and asserts that the displayed node ID for the out-of-rootpath test
does not gain a phantom aa/ prefix. The full testing/test_config.py
and testing/test_terminal.py suites pass locally on Windows
(Python 3.13) alongside testing/test_collection.py,
testing/test_session.py, testing/test_nodes.py,
testing/test_reports.py, and testing/test_skipping.py
(841 passed total).

Notes

This is my first contribution to pytest; happy to adjust the comment
wording, changelog phrasing, or test placement if preferred.

…3254)

When a test file lives outside rootpath, pytest computes its nodeid
by stripping one of the initial paths rather than rootpath (via
_check_initialpaths_for_relpath in nodes.py). Config.cwd_relative_nodeid
then assumed the nodeid was rootpath-relative and rebuilt the display
path as rootpath / nodeid, producing a string that pointed to a file
that does not exist on disk (e.g. aa/test_b.py::test_b when the test
is actually at ../../b/bb/test_b.py).

This change checks whether the synthesized fullpath exists; if not,
cwd_relative_nodeid returns the nodeid unchanged. The "<- ..."
location hint emitted by the terminal reporter still points to the
real file, so users retain the correct location information without
the misleading prefix.

Adds a regression test under TestNodeIDHandling in test_terminal.py
and a 13254.bugfix.rst changelog fragment.
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect paths to tests in output when using multiple test locations and --rootdir/pytest.ini

1 participant