diff --git a/AUTHORS b/AUTHORS index 3f43c7479..a4ce2e8e6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -97,6 +97,7 @@ Jon Sonesen Jonas Obrist Jordan Guymon Jordan Moldow +Jordan Speicher Joshua Bronson Jurko Gospodnetić Justyna Janczyszyn diff --git a/_pytest/main.py b/_pytest/main.py index f3dbd4344..97c6e276c 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -66,6 +66,8 @@ def pytest_addoption(parser): help="try to interpret all arguments as python packages.") group.addoption("--ignore", action="append", metavar="path", help="ignore path during collection (multi-allowed).") + group.addoption("--deselect", action="append", metavar="nodeid_prefix", + help="deselect item during collection (multi-allowed).") # when changing this to --conf-cut-dir, config.py Conftest.setinitial # needs upgrading as well group.addoption('--confcutdir', dest="confcutdir", default=None, @@ -208,6 +210,24 @@ def pytest_ignore_collect(path, config): return False +def pytest_collection_modifyitems(items, config): + deselect_prefixes = tuple(config.getoption("deselect") or []) + if not deselect_prefixes: + return + + remaining = [] + deselected = [] + for colitem in items: + if colitem.nodeid.startswith(deselect_prefixes): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + @contextlib.contextmanager def _patched_find_module(): """Patch bug in pkgutil.ImpImporter.find_module diff --git a/changelog/3198.feature.rst b/changelog/3198.feature.rst new file mode 100644 index 000000000..3c7838302 --- /dev/null +++ b/changelog/3198.feature.rst @@ -0,0 +1 @@ +Add command line option ``--deselect`` to allow deselection of individual tests at collection time. diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index c9d31d7c4..fc8dbf1b5 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -39,6 +39,14 @@ you will see that ``pytest`` only collects test-modules, which do not match the ======= 5 passed in 0.02 seconds ======= +Deselect tests during test collection +------------------------------------- + +Tests can individually be deselected during collection by passing the ``--deselect=item`` option. +For example, say ``tests/foobar/test_foobar_01.py`` contains ``test_a`` and ``test_b``. +You can run all of the tests within ``tests/`` *except* for ``tests/foobar/test_foobar_01.py::test_a`` +by invoking ``pytest`` with ``--deselect tests/foobar/test_foobar_01.py::test_a``. +``pytest`` allows multiple ``--deselect`` options. Keeping duplicate paths specified from command line ---------------------------------------------------- diff --git a/testing/test_session.py b/testing/test_session.py index 68534b102..32d8ce689 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -240,6 +240,20 @@ def test_exclude(testdir): result.stdout.fnmatch_lines(["*1 passed*"]) +def test_deselect(testdir): + testdir.makepyfile(test_a=""" + import pytest + def test_a1(): pass + @pytest.mark.parametrize('b', range(3)) + def test_a2(b): pass + """) + result = testdir.runpytest("-v", "--deselect=test_a.py::test_a2[1]", "--deselect=test_a.py::test_a2[2]") + assert result.ret == 0 + result.stdout.fnmatch_lines(["*2 passed, 2 deselected*"]) + for line in result.stdout.lines: + assert not line.startswith(('test_a.py::test_a2[1]', 'test_a.py::test_a2[2]')) + + def test_sessionfinish_with_start(testdir): testdir.makeconftest(""" import os