diff --git a/CHANGELOG b/CHANGELOG index 349e072cd..963f579bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,13 @@ or no tests were run at all (this is a partial fix for issue500). Thanks Eric Siegerman. +- New `testdirs` ini option: list of directories to search for tests + when executing pytest from the root directory. This can be used + to speed up test collection when a project has well specified directories + for tests, being usually more practical than configuring norecursedirs for + all directories that do not contain tests. + Thanks to Adrian for idea (#694) and Bruno Oliveira for the PR. + - fix issue713: JUnit XML reports for doctest failures. Thanks Punyashloka Biswal. diff --git a/_pytest/config.py b/_pytest/config.py index a717c5f0a..f89e2c592 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -917,7 +917,11 @@ class Config(object): self.hook.pytest_cmdline_preparse(config=self, args=args) args = self._parser.parse_setoption(args, self.option) if not args: - args.append(os.getcwd()) + cwd = os.getcwd() + if cwd == self.rootdir: + args = self.getini('testpaths') + if not args: + args = [cwd] self.args = args def addinivalue_line(self, name, line): diff --git a/_pytest/main.py b/_pytest/main.py index d84370e64..ead63535a 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -25,6 +25,8 @@ name_re = re.compile("^[a-zA-Z_]\w*$") def pytest_addoption(parser): parser.addini("norecursedirs", "directory patterns to avoid for recursion", type="args", default=['.*', 'CVS', '_darcs', '{arch}', '*.egg']) + parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.", + type="args", default=[]) #parser.addini("dirpatterns", # "patterns specifying possible locations of test files", # type="linelist", default=["**/test_*.txt", diff --git a/doc/en/customize.txt b/doc/en/customize.txt index d32712308..b92194999 100644 --- a/doc/en/customize.txt +++ b/doc/en/customize.txt @@ -157,6 +157,25 @@ Builtin configuration file options This would tell ``pytest`` to not look into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory. +.. confval:: testpaths + + .. versionadded:: 2.8 + + Sets list of directories that should be searched for tests when + no specific directories or files are given in the command line when + executing pytest from the :ref:`rootdir ` directory. + Useful when all project tests are in a known location to speed up + test collection and to avoid picking up undesired tests by accident. + + .. code-block:: ini + + # content of pytest.ini + [pytest] + testpaths = testing doc + + This tells pytest to only look for tests in ``testing`` and ``doc`` + directories when executing from the root directory. + .. confval:: python_files One or more Glob-style file patterns determining which python files diff --git a/doc/en/goodpractises.txt b/doc/en/goodpractises.txt index 3ec36219d..a62677fea 100644 --- a/doc/en/goodpractises.txt +++ b/doc/en/goodpractises.txt @@ -282,8 +282,11 @@ Conventions for Python test discovery ``pytest`` implements the following standard test discovery: -* collection starts from the initial command line arguments - which may be directories, filenames or test ids. +* collection starts from paths specified in :confval:`testpaths` if configured, + otherwise from initial command line arguments which may be directories, + filenames or test ids. If :confval:`testpaths` is not configured and no + directories or files were given in the command line, start collection from + the current directory. * recurse into directories, unless they match :confval:`norecursedirs` * ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_. * ``Test`` prefixed test classes (without an ``__init__`` method) diff --git a/testing/test_collection.py b/testing/test_collection.py index dca0fc8db..a7cb8a8c4 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -116,6 +116,35 @@ class TestCollectFS: rec = testdir.inline_run("xyz123/test_2.py") rec.assertoutcome(failed=1) + def test_testpaths_ini(self, testdir, monkeypatch): + testdir.makeini(""" + [pytest] + testpaths = gui uts + """) + tmpdir = testdir.tmpdir + tmpdir.ensure("env", "test_1.py").write("def test_env(): pass") + tmpdir.ensure("gui", "test_2.py").write("def test_gui(): pass") + tmpdir.ensure("uts", "test_3.py").write("def test_uts(): pass") + + # executing from rootdir only tests from `testpaths` directories + # are collected + items, reprec = testdir.inline_genitems('-v') + assert [x.name for x in items] == ['test_gui', 'test_uts'] + + # check that explicitly passing directories in the command-line + # collects the tests + for dirname in ('env', 'gui', 'uts'): + items, reprec = testdir.inline_genitems(tmpdir.join(dirname)) + assert [x.name for x in items] == ['test_%s' % dirname] + + # changing cwd to each subdirectory and running pytest without + # arguments collects the tests in that directory normally + for dirname in ('env', 'gui', 'uts'): + monkeypatch.chdir(testdir.tmpdir.join(dirname)) + items, reprec = testdir.inline_genitems() + assert [x.name for x in items] == ['test_%s' % dirname] + + class TestCollectPluginHookRelay: def test_pytest_collect_file(self, testdir): wascalled = []