如何在pytest中全局打补丁?

问题描述 投票:0回答:2

我在代码中经常使用 pytest。示例代码结构如下所示。整个代码库是

python-2.7

core/__init__.py
core/utils.py

#feature

core/feature/__init__.py
core/feature/service.py

#tests
core/feature/tests/__init__.py
core/feature/tests/test1.py
core/feature/tests/test2.py
core/feature/tests/test3.py
core/feature/tests/test4.py
core/feature/tests/test10.py

service.py
看起来像这样:

from modules import stuff
from core.utils import Utility


class FeatureManager:
    # lots of other methods
    def execute(self, *args, **kwargs):
        self._execute_step1(*args, **kwargs)
        # some more code
        self._execute_step2(*args, **kwargs)
        utility = Utility()
        utility.doThings(args[0], kwargs['variable'])

feature/tests/*
中的所有测试最终都使用
core.feature.service.FeatureManager.execute
函数。但是,在运行测试时,我不需要运行
utility.doThings()
。我需要它在生产应用程序运行时发生,但我不希望它在测试运行时发生。

我可以在我的

core/feature/tests/test1.py

中做这样的事情
from mock import patch

class Test1:
   def test_1():
       with patch('core.feature.service.Utility') as MockedUtils:
           exectute_test_case_1()

这会起作用。不过,我刚刚将

Utility
添加到代码库中,并且我有超过 300 个测试用例。我不想进入每个测试用例并编写这个
with
声明。

我可以编写一个

conftest.py
来设置操作系统级环境变量,根据该变量
core.feature.service.FeatureManager.execute
可以决定不执行
utility.doThings
但我不知道这是否是解决此问题的干净解决方案。

如果有人可以帮助我对整个会话进行全局补丁,我将不胜感激。我想在整个会话期间在全局范围内执行我对上面的

with
块所做的操作。任何有关此事的文章也都很棒。

TLDR:如何在运行 pytest 时创建会话范围的补丁?

python tdd pytest python-mock
2个回答
25
投票

我添加了一个名为

core/feature/conftest.py
的文件,如下所示

import logging
import pytest
from unittest import mock


@pytest.fixture(scope="session", autouse=True)
def default_session_fixture(request):
    """
    :type request: _pytest.python.SubRequest
    :return:
    """
    log.info("Patching core.feature.service")
    patched = mock.patch('core.feature.service.Utility')
    patched.__enter__()

    def unpatch():
        patched.__exit__()
        log.info("Patching complete. Unpatching")

    request.addfinalizer(unpatch)

这并不复杂。就像做一样

with mock.patch('core.feature.service.Utility') as patched:
    do_things()

但仅限于整个会话范围内。


16
投票

基于类似用例(4.5年后)的当前接受的答案,使用unittest.mock的

patch
yield
也有效:

from typing import Iterator
from unittest.mock import patch

import pytest


@pytest.fixture(scope="session", autouse=True)
def default_session_fixture() -> Iterator[None]:
    log.info("Patching core.feature.service")
    with patch("core.feature.service.Utility"):
        yield
    log.info("Patching complete. Unpatching")

旁白

对于这个答案,我使用了

autouse=True
。在我的实际用例中,为了在逐个测试的基础上集成到我的单元测试中,我使用了
@pytest.mark.usefixtures("default_session_fixture")


版本

Python==3.8.6
pytest==6.2.2
© www.soinside.com 2019 - 2024. All rights reserved.