How can I import a testclass properly to inherit from, without it being run as a test

Context

I have a test class where all my tests inherit from. It cant run by itself as it really doesnt contain any setup info

I wanted to add a test which is executed by ALL tests (adding it to the baseclass seems logical)

But now I notice the basetestclass( => Foo) which I import is being detected as a test itself and runs and is visible in the reports

Code

the base class in base.py

from unittest import TestCase

class Foo(TestCase):
    @classmethod
    def setUpClass(cls):
        # prepare the generic setup stuff based on what is defined in the child class
        print("setupclass Foo done")

    def test_run_in_all_inherited_tests(self):
        print("fooBar")
        assert True

the real test in test_something.py

from base import Foo # <= This is being detected as a testclass object and thus will be executed

class TestFoo(Foo):
    @classmethod
    def setUpClass(cls):
        # define specific test setup
        super().setUpClass()
        print("setup TestFoo done")

    def test_pass(self):
        pass

    def test_assert(self):
        assert False

This triggers a testrun of the imported Foo

pycharm testrun

The Question

How can I import Foo without that its being detected as a ‘test’ If I remove the test to run in all tests all is fine.

Adding @nottest decorator to Foo wont work since then also all inherited classes are defined nottest.

It needs to run on nose, pytest and unittest testrunners

I noticed if I changed the import statement like below that it also works. But that would mean adjusting a few hundreds of testfiles in different repos. (I’d like to avoid that)

import base
class TestFoo(base.Foo):

Answer

The key to the answer seems to be that each test has an attribute __test__ which is set to True when it is a test.

Setting it to False when the class should not be a test will then let the test collector ignore this class.

The answer assumes I can only do changes in the base.py

In python 3.9 classmethod and property decorators can be combined so I wrote a separate answer for that

answer for < py3.9

the base class in base.py

from unittest import TestCase

class MetaFoo(type):
    @property
    def __test__(cls):
        return cls != Foo

class Foo(TestCase, metaclass=MetaFoo):
    @classmethod
    def setUpClass(cls):
        # prepare the generic setup stuff based on what is defined in the child class
        print("setupclass Foo done")

    def test_run_in_all_inherited_tests(self):
        print("fooBar")
        assert True

answer for >= py3.9

the base class in base.py

from unittest import TestCase

class Foo(TestCase, metaclass=MetaFoo):
    @classmethod
    @property
    def __test__(cls):
        return cls != Foo

    @classmethod
    def setUpClass(cls):
        # prepare the generic setup stuff based on what is defined in the child class
        print("setupclass Foo done")

    def test_run_in_all_inherited_tests(self):
        print("fooBar")
        assert True

the actual test

test_something.py

from base import Foo # <= This will not be detected as a test anymore as __test__ returns False

class TestFoo(Foo):
    @classmethod
    def setUpClass(cls):
        # define specific test setup
        super().setUpClass()
        print("setup TestFoo done")

    def test_pass(self):
        pass

    def test_assert(self):
        assert False

This doesnt trigger a testrun of the imported Foo anymore pycharm test run