在执行 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):
    ...