You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在pytest收集前Mock diskcache.Cache以测试缓存逻辑

解决pytest中Mock diskcache.Cache时机过晚的问题

你的核心问题在于:my_module在导入阶段就已经用真实的diskcache.Cache完成了expensive_function的装饰,而原来的autouse fixture是在测试运行阶段才执行Mock,此时装饰器早已生效,Mock无法覆盖已有的装饰逻辑。

要解决这个问题,需要在pytest测试收集阶段之前完成Mock,确保模块导入时就使用Mock后的Cache类。以下是具体实现方案:

修改后的conftest.py代码

import pytest
import unittest.mock
import diskcache
import importlib
import my_module  # 替换为你的实际模块名

class MockCache:
    def __init__(self, *args, **kwargs):
        pass

    def memoize(self, fun=None, **kwargs):
        # 兼容带参数的装饰器写法(如@cache.memoize(expire=3600))
        if fun is None:
            return lambda f: f
        return fun

def pytest_configure(config):
    if not config.getoption("--cache"):
        # 在测试收集前替换diskcache.Cache为MockCache
        mock_cache_patch = unittest.mock.patch.object(diskcache, 'Cache', MockCache)
        mock_cache_patch.start()
        # 重新加载模块,确保装饰器使用Mock后的Cache
        importlib.reload(my_module)
        # 保存Mock对象,用于后续清理
        config._diskcache_mock = mock_cache_patch

def pytest_unconfigure(config):
    # 测试结束后停止Mock,避免污染环境
    if hasattr(config, '_diskcache_mock'):
        config._diskcache_mock.stop()

def pytest_addoption(parser):
    parser.addoption(
        "--cache",
        action="store_true",
        default=False,
        help=(
            "Enable caching for tests. Careful: This will disable the test coverage "
            "of the cached code if the return value is in cache."
        ),
    )

关键细节说明

  1. 使用pytest_configure钩子
    这个钩子在pytest启动后、测试用例收集前执行,是能提前Mock的最早时机,确保模块导入时就使用Mock后的Cache类。

  2. 重新加载目标模块
    若pytest启动时目标模块已被预导入(比如测试文件中提前import了),必须用importlib.reload重新加载模块,让装饰器重新应用Mock后的memoize方法。

  3. 修正MockCache的memoize实现
    真实的diskcache.Cache.memoize是实例方法,且支持带参数的装饰器写法(如expire=3600),因此Mock类的memoize需要处理两种场景:直接装饰函数、先接收参数再返回装饰器。

  4. 清理Mock资源
    通过pytest_unconfigure钩子在测试结束后停止Mock,避免影响后续测试或全局环境。

测试用例示例

# test_my_module.py
from my_module import expensive_function
from unittest.mock import patch

def test_expensive_function_without_cache():
    # 验证缓存被禁用时,函数每次调用都会执行真实逻辑
    with patch('my_module.expensive_function') as mock_func:
        mock_func.return_value = "test_result"
        expensive_function(1)
        expensive_function(1)
        assert mock_func.call_count == 2

def test_expensive_function_with_cache():
    # 运行时添加--cache参数,验证缓存生效(函数仅执行一次)
    with patch('my_module.expensive_function') as mock_func:
        mock_func.return_value = "test_result"
        expensive_function(1)
        expensive_function(1)
        assert mock_func.call_count == 1

内容的提问来源于stack exchange,提问作者Daniel Böckenhoff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:55:59