Skip to content

Commit

Permalink
Consider testpaths for initial conftests
Browse files Browse the repository at this point in the history
The 'testpaths' option is meant to be identical to execute
pytest passing the 'testpaths' directories explicitly.

Fix #10987
  • Loading branch information
nicoddemus committed May 12, 2023
1 parent b241c0b commit faeb161
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelog/10987.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:confval:`testpaths` is now honored to load root ``conftests``.
16 changes: 12 additions & 4 deletions doc/en/reference/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1713,13 +1713,12 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: testpaths



Sets list of directories that should be searched for tests when
no specific directories, files or test ids are given in the command line when
executing pytest from the :ref:`rootdir <rootdir>` directory.
File system paths may use shell-style wildcards, including the recursive
``**`` pattern.

Useful when all project tests are in a known location to speed up
test collection and to avoid picking up undesired tests by accident.

Expand All @@ -1728,8 +1727,17 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
testpaths = testing doc
This tells pytest to only look for tests in ``testing`` and ``doc``
directories when executing from the root directory.
This configuration means that executing:

.. code-block:: console
pytest
has the same practical effects as executing:

.. code-block:: console
pytest testing doc
.. confval:: tmp_path_retention_count
Expand Down
26 changes: 22 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,10 @@ def pytest_configure(self, config: "Config") -> None:
# Internal API for local conftest plugin handling.
#
def _set_initial_conftests(
self, namespace: argparse.Namespace, rootpath: Path
self,
namespace: argparse.Namespace,
rootpath: Path,
testpaths_ini: Sequence[str],
) -> None:
"""Load initial conftest files given a preparsed "namespace".
Expand All @@ -543,7 +546,7 @@ def _set_initial_conftests(
)
self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir
testpaths = namespace.file_or_dir + testpaths_ini
foundanchor = False
for testpath in testpaths:
path = str(testpath)
Expand All @@ -552,7 +555,20 @@ def _set_initial_conftests(
if i != -1:
path = path[:i]
anchor = absolutepath(current / path)
if anchor.exists(): # we found some file object

# On Python 3.7 on Windows, anchor.exists() might raise
# if the anchor contains glob characters (for example "*//tests"), specially
# in the case of the 'testpaths' ini option.
# Using an explicit version check to remove this code later once
# Python 3.7 is dropped.
if sys.version_info[:2] == (3, 7):
try:
anchor_exists = anchor.exists()
except OSError: # pragma: no cover
anchor_exists = False
else:
anchor_exists = anchor.exists()
if anchor_exists: # We found some file object.
self._try_load_conftest(anchor, namespace.importmode, rootpath)
foundanchor = True
if not foundanchor:
Expand Down Expand Up @@ -1131,7 +1147,9 @@ def _processopt(self, opt: "Argument") -> None:
@hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
self.pluginmanager._set_initial_conftests(
early_config.known_args_namespace, rootpath=early_config.rootpath
early_config.known_args_namespace,
rootpath=early_config.rootpath,
testpaths_ini=self.getini("testpaths"),
)

def _initini(self, args: Sequence[str]) -> None:
Expand Down
23 changes: 23 additions & 0 deletions testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,29 @@ def test_collect_pyargs_with_testpaths(
result.stdout.fnmatch_lines(["*1 passed in*"])


def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
"""The testpaths ini option should load conftests in those paths as 'initial' (#10987)."""
p = pytester.mkdir("some_path")
p.joinpath("conftest.py").write_text(
textwrap.dedent(
"""
def pytest_sessionstart(session):
raise Exception("pytest_sessionstart hook is successfully run")
"""
)
)
pytester.makeini(
"""
[pytest]
testpaths = some_path
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(
"INTERNALERROR* Exception: pytest_sessionstart hook is successfully run"
)


def test_collect_symlink_file_arg(pytester: Pytester) -> None:
"""Collect a direct symlink works even if it does not match python_files (#4325)."""
real = pytester.makepyfile(
Expand Down
2 changes: 1 addition & 1 deletion testing/test_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self) -> None:
self.importmode = "prepend"

namespace = cast(argparse.Namespace, Namespace())
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]))
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])


@pytest.mark.usefixtures("_sys_snapshot")
Expand Down

0 comments on commit faeb161

Please sign in to comment.