如何正确导入测试类以继承,而不将其作为测试运行

stu*_*ioj 5 python inheritance nose pytest python-unittest

语境

我有一个测试类,我的所有测试都继承自该类。它无法自行运行,因为它实际上不包含任何设置信息

我想添加一个由所有测试执行的测试(将其添加到基类似乎合乎逻辑)

但现在我注意到我导入的 basetestclass( => Foo) 被检测为测试本身并运行并在报告中可见

代码

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
Run Code Online (Sandbox Code Playgroud)

真正的测试在 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
Run Code Online (Sandbox Code Playgroud)

这会触发导入的 Foo 的测试运行

pycharm测试运行

问题

如何导入 Foo 而不将其检测为“测试”如果我删除测试以在所有测试中运行,一切都很好。

向 Foo添加@nottest装饰器将不起作用,因为所有继承的类都被定义为非测试。

它需要在nose,pytestunittest测试运行器上运行

我注意到,如果我像下面这样更改导入语句,它也可以工作。但这意味着需要调整不同存储库中的数百个测试文件。(我想避免这种情况)

import base
class TestFoo(base.Foo):
Run Code Online (Sandbox Code Playgroud)

stu*_*ioj 5

答案的关键似乎是每个测试都有一个在测试时__test__设置的属性。True

将其设置为False当类不应该是测试时将使测试收集器忽略此类。

答案假设我只能在 base.py 中进行更改

在 python 3.9 中,类方法和属性装饰器可以组合,所以我为此写了一个单独的答案

回答 < py3.9

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
Run Code Online (Sandbox Code Playgroud)

回答 >= py3.9

base.py 中的基类

from unittest import TestCase

class Foo(TestCase):
    @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
Run Code Online (Sandbox Code Playgroud)

实际测试

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
Run Code Online (Sandbox Code Playgroud)

这不会再触发导入的 Foo 的测试运行 pycharm测试运行