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
这样您就可以在收集时拦截测试并注入您自己的参数化设置。每个测试都会调用该钩子,并以 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 文档展示了如何使用命令行中的数据进行参数化,这可能比上述方法更有用,但我试图按照您的指导使用代码定义数据。
我正在尝试重用一些单元测试功能。唯一可变的部分实际上是测试数据集。因此,我将测试逻辑封装在 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
这样您就可以在收集时拦截测试并注入您自己的参数化设置。每个测试都会调用该钩子,并以 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 文档展示了如何使用命令行中的数据进行参数化,这可能比上述方法更有用,但我试图按照您的指导使用代码定义数据。