使用依赖注入将 PyTest fixture 添加到测试 class

Add PyTest fixtures to a test class using dependency injection

我将 Python 3 与 PyTest 一起使用,并在 conftest.py 中定义了许多固定对象。我遇到的问题是每个测试用例都需要一些固定对象。在所有测试用例中请求这些夹具引用会导致大量重复的样板代码。

这是 conftest.py 中的固定装置:

def fixtureA(request):
    _fixture = FixtureA()
    # initialize the fixture, do stuff with request
    return _fixture

这是当前测试 class,为了便于阅读,我想从参数中删除所有固定装置:

class TestSomeFeature(BaseTest):

    def test_some_function(self, fixtureA, fixtureB, fixtureC):
        fixtureA.doSomething()
        # execute test case, do stuff with other fixtures     

    def test_some_other_function(self, fixtureA, fixtureB, fixtureC):
        data = fixtureB.getData()
        # execute test case

这种方法有效,但我想找到一种方法来使用依赖注入(或类似方法)自动将固定装置注入 BaseTest 属性中,而不必在每个测试用例的参数列表中指定它们。我正在寻找类似的东西,但愿意接受任何其他建议:

class BaseTest:
    # This does not work, since pytest does not allow constructors in the test class
    def __init__(fixtureA, fixtureB, fixtureC):
        self.fixtureA = fixtureA
        self.fixtureB = fixtureB
        self.fixtureC = fixtureC

我希望测试 class 看起来像这样,干净多了!

class TestSomeFeature(BaseTest):

    def test_some_function(self):
        self.FixtureA.doSomething()
        # execute test case

    def test_some_other_function(self):
        data = self.FixtureB.getData()
        # execute test case

首先,您可以在 conftest.py 和测试 class 中定义固定装置。不同之处在于可见性:如果您在 conftest.py 中定义夹具,则该 conftest.py 文件级别及以下级别的所有测试都可以看到它。如果您在测试模块中定义它,则它仅在该模块中可见。如果您在测试 class 中定义它,它在此 class 中可见并派生 classes.

另请注意,如果您 return 一个值,您也可以使用 autotest=True - 您只需要在相应的测试中引用夹具。您还可以将夹具值保存在变量中。如果您使用的是基数 class:

,则这是两种情况的简单示例
class TestBase:
    @pytest.fixture(autouse=True)
    def fixture1(self):
        self.value1 = 1  # save the fixture value
        yield

    @pytest.fixture
    def fixture2(self):
        yield 2  # return the fixture value - fixtue has to be referenced
        # autouse can still make sense if there is setup/tearDown code,
        # and the fixture must not be referenced in all of the tests

    @pytest.fixture(autouse=True)
    def fixture3(self):
        self.value3 = 3
        yield 3  # do both - can be used either way


class TestDerived(TestBase):
    def test_1(self):
        assert self.value1 == 1

    def test_2(self, fixture2):
        assert fixture2 == 2

    def test_3_1(self):
        assert self.value3 == 3

    def test_3_2(self, fixture3):
        assert fixture3 == 3

注意你得到的是夹具值,而不是夹具本身,如果你引用夹具,所以没有必要(也不可能)调用 fixture - 而不是直接使用由 fixture 编辑的值 return。

以下代码基于@MrBeanBremen 的回答。

您可以在基础 class 中创建一个名为 injector 的夹具,其唯一职责是将 conftest 中的夹具注入基础 class:

class BaseTest:

    @fixture(autouse=True)
    # noinspection PyAttributeOutsideInit
    def injector(self, fixtureA, fixtureB):
        self.fixtureA = fixtureA
        self.fixtureB = fixtureB

所有继承自 BaseTest 的测试 classes 现在无需任何样板代码即可访问固定装置。 BaseTest 中不能使用构造函数,因为如果实现了构造函数,PyTest 将完全忽略测试 class。 PyCharm 将生成弱警告,因为我们在 __init__ 之外定义属性,但可以使用 noinspection 注释来抑制。