在pytes中模拟模块级函数

2024-04-26 17:40:41 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个函数有一个装饰器。decorator接受参数,参数的值从另一个函数调用派生。在

在示例.py在

from cachetools import cached
from cachetools import TTLCache

from other import get_value


@cached(cache=TTLCache(maxsize=1, ttl=get_value('cache_ttl')))
def my_func():
    return 'result'

在其他.py在

^{pr2}$

我想模拟对get_value()的调用。我在测试中使用了以下内容:

示例_测试.py在

import mock
import pytest

from example import my_func


@pytest.fixture
def mock_get_value():
    with mock.patch(
        "example.get_value",
        autospec=True,
    ) as _mock:
        yield _mock


def test_my_func(mock_get_value):
    assert my_func() == 'result'

我在这里注入mock_get_value来测试我的函数。但是,由于我的decorator在第一次导入时被调用,get_value()立即被调用。有没有办法在使用pytest立即导入模块之前模拟对get_value()的调用吗?在


Tags: 函数frompyimport示例参数getpytest
1条回答
网友
1楼 · 发布于 2024-04-26 17:40:41

from example import my_func移到测试函数的with内。同时在它真正的来源处进行修补,other.get_value。这可能就是一切。在


Python在sys.modules中缓存模块,因此模块级代码(如函数定义)只在的第一个导入上运行。如果这不是第一次,您可以使用importlib.reload()或删除sys.modules中的相应键并再次导入来强制重新导入。在

请注意,重新导入模块可能会有副作用,您可能还希望在运行测试后重新导入模块,以避免干扰其他测试。如果另一个模块正在使用在重新导入的模块中定义的对象,这些对象不仅会消失,而且可能不会按预期的方式更新。例如,重新导入一个模块可能会创建本应是单例的第二个实例。在

一种更健壮的方法是将原始导入的模块对象保存到其他地方,从sys.modules中删除,在测试期间使用修补过的版本重新导入,然后在测试之后将原始导入放回sys.modules。您可以通过在sys.modules上的patch.dict()上下文内部导入来完成此操作。在

import mock
import sys

import pytest

@pytest.fixture
def mock_get_value():
    with mock.patch(
        "other.get_value",
        autospec=True,
    ) as _mock, mock.patch.dict("sys.modules"):
        sys.modules.pop("example", None)
        yield _mock


def test_my_func(mock_get_value):
    from example import my_func
    assert my_func() == 'result'

另一种可能是在测试中,在原始函数上调用decorator。如果decorator使用了functools.wraps()/functools.update_wrapper(),那么原始函数应该作为__wrapped__属性使用。这可能不可用,具体取决于decorator是如何实现的。在

相关问题 更多 >