在执行 pytest fixture 之前获取 运行 的模拟
getting a mock to run before a pytest fixture is executed
我正在尝试针对不同的测试在函数范围的夹具中以不同方式有条件地模拟某些东西,因此我不必使用几乎相同的代码创建两个完全独立的夹具。
MY_GLOB_VAR = False
def mocked_build_data():
with conditional(MY_GLOB_VAR, mock.patch(stuff)):
build_data()
class Tests:
@pytest.fixture(scope='function')
def my_fixture(self, conftest_fixtures):
inject_test_specific_data_into_db(conftest_fixtures)
mocked_build_data()
yield
cleanup_mysql()
def test_1(self, my_fixture):
assert True
@mock.patch(f'{__name__}.MY_GLOB_VAR', True)
def test_2(self, my_fixture):
assert True
我需要在 test_2
上应用模拟 在 my_fixture
执行之前,但它只会在我进入函数体时应用.
我可以按照我正在做的方式强制执行此操作,还是以其他方式有条件地模拟夹具?
您可以为此使用 Pytest 的 indirect parametrization 功能。
Using the indirect=True
parameter when parametrizing a test allows to parametrize a test with a fixture receiving the values before passing them to a test.
在您的情况下,您可以根据它接收到的值(通过 request
参数)模拟夹具中的对象。下面举例说明原理。
import pytest
MY_GLOB_VAR = None
@pytest.fixture(scope="function")
def my_fixture(request):
if hasattr(request, "param"):
# use it without param (default case)
global MY_GLOB_VAR
MY_GLOB_VAR = request.param
yield
# restore initial status
MY_GLOB_VAR = None
@pytest.mark.parametrize("my_fixture", [True], indirect=True)
def test_true(my_fixture):
assert MY_GLOB_VAR == True
@pytest.mark.parametrize("my_fixture", [False], indirect=True)
def test_false(my_fixture):
assert MY_GLOB_VAR == False
def test_none(my_fixture):
"""Example without parametrization"""
assert MY_GLOB_VAR is None
运行 测试
pytest test_mock.py
# test_mock.py::test_true[True] PASSED
# test_mock.py::test_false[False] PASSED
# test_mock.py::test_none PASSED ```
您还可以在单独的装置中移动修补程序,并确保在 test_2
中的 my_fixture
之前请求它。夹具按照它们在测试函数参数中列出的顺序执行,因此在签名 def test(fix1, fix2):
中,夹具 fix1
将在 fix2
等之前执行。专门针对您的示例:
MY_GLOB_VAR = False
class Tests:
@pytest.fixture
def patch_my_glob_var(self):
with mock.patch(f'{__name__}.MY_GLOB_VAR', True):
yield
@pytest.fixture(scope='function')
def my_fixture(self):
print('MY_GLOB_VAR is', MY_GLOB_VAR)
yield
def test_1(self, my_fixture):
assert MY_GLOB_VAR is False
def test_2(self, patch_my_glob_var, my_fixture):
assert MY_GLOB_VAR is True
运行 带有 -s
标志的它应该产生
some_test_module::Tests::test_1 MY_GLOB_VAR is False
PASSED
some_test_module::Tests::test_2 MY_GLOB_VAR is True
PASSED
另一种选择是通过 usefixtures
标记请求夹具,因为其返回的结果未在测试中明确使用。但是,这将导致相同的结果,因为 patch_my_glob_var
仍将在 my_fixture
:
之前进行评估
@pytest.mark.usefixtures('patch_my_glob_var')
def test_2(self, my_fixture):
...
我正在尝试针对不同的测试在函数范围的夹具中以不同方式有条件地模拟某些东西,因此我不必使用几乎相同的代码创建两个完全独立的夹具。
MY_GLOB_VAR = False
def mocked_build_data():
with conditional(MY_GLOB_VAR, mock.patch(stuff)):
build_data()
class Tests:
@pytest.fixture(scope='function')
def my_fixture(self, conftest_fixtures):
inject_test_specific_data_into_db(conftest_fixtures)
mocked_build_data()
yield
cleanup_mysql()
def test_1(self, my_fixture):
assert True
@mock.patch(f'{__name__}.MY_GLOB_VAR', True)
def test_2(self, my_fixture):
assert True
我需要在 test_2
上应用模拟 在 my_fixture
执行之前,但它只会在我进入函数体时应用.
我可以按照我正在做的方式强制执行此操作,还是以其他方式有条件地模拟夹具?
您可以为此使用 Pytest 的 indirect parametrization 功能。
Using the
indirect=True
parameter when parametrizing a test allows to parametrize a test with a fixture receiving the values before passing them to a test.
在您的情况下,您可以根据它接收到的值(通过 request
参数)模拟夹具中的对象。下面举例说明原理。
import pytest
MY_GLOB_VAR = None
@pytest.fixture(scope="function")
def my_fixture(request):
if hasattr(request, "param"):
# use it without param (default case)
global MY_GLOB_VAR
MY_GLOB_VAR = request.param
yield
# restore initial status
MY_GLOB_VAR = None
@pytest.mark.parametrize("my_fixture", [True], indirect=True)
def test_true(my_fixture):
assert MY_GLOB_VAR == True
@pytest.mark.parametrize("my_fixture", [False], indirect=True)
def test_false(my_fixture):
assert MY_GLOB_VAR == False
def test_none(my_fixture):
"""Example without parametrization"""
assert MY_GLOB_VAR is None
运行 测试
pytest test_mock.py
# test_mock.py::test_true[True] PASSED
# test_mock.py::test_false[False] PASSED
# test_mock.py::test_none PASSED ```
您还可以在单独的装置中移动修补程序,并确保在 test_2
中的 my_fixture
之前请求它。夹具按照它们在测试函数参数中列出的顺序执行,因此在签名 def test(fix1, fix2):
中,夹具 fix1
将在 fix2
等之前执行。专门针对您的示例:
MY_GLOB_VAR = False
class Tests:
@pytest.fixture
def patch_my_glob_var(self):
with mock.patch(f'{__name__}.MY_GLOB_VAR', True):
yield
@pytest.fixture(scope='function')
def my_fixture(self):
print('MY_GLOB_VAR is', MY_GLOB_VAR)
yield
def test_1(self, my_fixture):
assert MY_GLOB_VAR is False
def test_2(self, patch_my_glob_var, my_fixture):
assert MY_GLOB_VAR is True
运行 带有 -s
标志的它应该产生
some_test_module::Tests::test_1 MY_GLOB_VAR is False
PASSED
some_test_module::Tests::test_2 MY_GLOB_VAR is True
PASSED
另一种选择是通过 usefixtures
标记请求夹具,因为其返回的结果未在测试中明确使用。但是,这将导致相同的结果,因为 patch_my_glob_var
仍将在 my_fixture
:
@pytest.mark.usefixtures('patch_my_glob_var')
def test_2(self, my_fixture):
...