Skip to content

Don't auto-discover tests in particular folders #12749

@wpietri

Description

@wpietri

This inspired by a discussion: #12748

What's the problem this feature will solve?

We have a project that's about a different kind of test. We have a lot of classes with "Test" in the name, including some that start with "Test". We would also like to organize some of our pytest tests in classes whose names start with "Test". We distinguish them by directory; all our pytest tests are in tests, and all our production code is in src. We'd like an easy way to set this up with no distortion to our production code and minimal shenanigans in the test code, while getting no warnings.

Naively one might think that limiting discovery to a particular directory, as with pytest tests or testpaths=["tests"]. However, if a production file called TestFoo is imported from production code into test code, it will still be discovered, resulting in a warning like:

src/pytest_collection_issue/domain.py:6
  src/pytest_collection_issue/domain.py:6: PytestCollectionWarning: cannot collect test class 'Testament' because it has a __init__ constructor (from: tests/test_domain.py)
    class Testament(object):

Describe the solution you'd like

For me, the best solution is just to by default disable discovery beyond the requested testpaths. Trying to run something just because it's included from wherever strikes me as counterintuitive behavior. If somebody needs that behavior, perhaps an option like discover_imports would be useful.

The second-best solution would be an option where we can specify paths to definitely not discover things in. E.g.:

testpaths = ["tests"]
excludepaths = ["src"]

Alternative Solutions

There are many ways to solve this, including manually adding a __test__ = false to each production class, using a marker mixin like NotAPytestTest to achieve the same end, importing production classes into the tests under different names, doing only local imports, or renaming our test classes. All of these are a little clunky, and require people to remember a ritual under certain circumstances.

The solution I went with is to put

for a_class in [i[1] for i in (globals().items()) if inspect.isclass(i[1])]:
    if a_class.__name__.startswith('Test'):
        a_class.__test__ = False

after my domain imports and before my test classes. It still seems clunky to me, but only has to happen once per file of tests. I think it would be better still if this could be fixed in one spot, or have it work by default.

Additional context

Please see this GitHub discussion and this example repository that demonstrates the problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions