pytest:如何使用测试 class 实例变量作为夹具参数

pytest: How to use test class instance variables as fixture params

我正在尝试重用一些单元测试功能。唯一可变的部分实际上是测试数据集。因此,我将测试逻辑封装在 class 中,并在对象创建时注入实际测试数据。

不幸的是,这似乎不适用于 @pytest.fixture 装饰器。

一些要重现的最少代码。 这是我的测试 class in define_test.py:

的通用定义
#!/usr/bin/env python3

import pytest

class TestDemo:
    def __init__(self, _data: list):
        self.__testData = _data

    @pytest.fixture(scope='module', params=self.__testData)
    def fixture(self, request):
        return request.param

    def test(self, fixture):
        foo = fixture
        assert foo is not None

use_test.py

中的预期用途是这样的
#!/usr/bin/env python3

import define_test

data1 = [1, 2, 3, 4]
test1 = define_test.TestDemo(data1)
data2 = [4, 3, 2, 1]
test2 = define_test.TestDemo(data2)

尝试 运行 此代码生成

define_test.py:9: in TestDemo
    ???
E   NameError: name 'self' is not defined

我很确定问题在于,不能简单地将通常的 OO 机制应用于 pytest 装饰器。似乎装饰器在这个 class 的实例存在之前就已经被评估,或者它根本无法处理实例。

那么使用可变数据实现可重用测试目标的正确方法是什么?

您可以通过动态参数化来表达这一点,您可以通过钩子访问它 pytest_generate_tests

还有一些examples in the docs

这样您就可以在收集时拦截测试并注入您自己的参数化设置。每个测试都会调用该钩子,并以 metafunc 对象作为公开 parameterize 方法

的参数

metafunc 文档是 here

在我看来,您无法访问实例数据,因为这段代码发生在收集时,在您的对象初始化之前。但是你可以通过 metafunc.cls 访问 class 对象,所以也许你可以在 superclass 中使用你的方法,然后 subclass 添加特定的数据案例作为 class 属性

这是我正在考虑的事情的一些示例代码

a) pytestfoo.py

#!/usr/bin/env python3

import pytest


class BaseTest:

    def test(self, testparams):
        print("\n", testparams, "\n")
        assert testparams is not None

class TestCaseA(BaseTest):
    testparams = [[1,2,3,4]]

class TestCaseB(BaseTest):
    testparams = [[4,3,2,1]]

b)对应的conftest.py

def pytest_generate_tests(metafunc):
    metafunc.parametrize("testparams", metafunc.cls.testparams)

和 运行 这些测试使用 pytest


pytest -sv pytestfoo.py
========================================================== test session starts ==========================================================
platform linux -- Python 3.7.6, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- /home/cms/.pyenv/versions/3.7.6/envs/tmp/bin/python3.7
cachedir: .pytest_cache
rootdir: /home/cms/tmp
collected 2 items                                                                                                                       

pytestfoo.py::TestCaseA::test[testparams0] 
 [1, 2, 3, 4] 

PASSED
pytestfoo.py::TestCaseB::test[testparams0] 
 [4, 3, 2, 1] 

PASSED

=========================================================== 2 passed in 0.01s ===========================================================

希望这里对您自己的实验有一些启发。 pytest 文档展示了如何使用命令行中的数据进行参数化,这可能比上述方法更有用,但我试图按照您的指导使用代码定义数据。