
在使用 pytest 测试被 `joblib.memory.cache` 装饰的函数时,可通过访问其 `.func` 属性绕过缓存,直接执行原始逻辑,确保测试真实、可重复且不依赖磁盘缓存状态。
joblib.Memory.cache 装饰器会将原函数封装为一个 MemorizedFunc 实例,该实例不仅支持缓存调用,还保留了对原始函数的引用——即 .func 属性。这是 joblib 官方提供的标准方式,无需引入 mocking(如 unittest.mock.patch 或 pytest-mock),既简洁又可靠。
例如,以下代码定义了一个带缓存的平方函数:
from joblib import Memory
memory = Memory(location="cache")
@memory.cache
def func(a):
print("Executing original function!") # 可用于验证是否真正执行
return a ** 2在单元测试中,若直接调用 func(2),joblib 可能从磁盘读取缓存结果(甚至首次运行后生成缓存文件),导致无法验证函数逻辑本身,也使测试耦合于外部状态。此时应改用:
def test_func():
assert func.func(2) == 4 # ✅ 直接调用原始函数,完全跳过缓存✅ 优势说明:
- 零副作用:不创建/读取缓存目录,无需清理 location;
- 高可靠性:不依赖 mock 行为,避免因 patch 错误目标(如误 mock Memory.__init__ 或 cache 方法)导致测试失效;
- 语义清晰:.func 是 joblib 公开 API,文档明确支持,长期兼容性好。
⚠️ 注意事项:
- 不要尝试 mock Memory 实例或重置 memory 对象(如 memory.clear()),这会影响其他测试的隔离性,且无法保证当前调用一定“不命中缓存”;
- 若需测试缓存行为本身(如验证是否命中/未命中),应另起测试用例,并配合 tmp_path 提供临时 cache 目录 + 显式清理;
- 确保被测函数未被多次装饰(如叠加 @lru_cache 或自定义装饰器),否则 .func 链可能断裂——此时建议重构为显式分离逻辑与缓存(如将核心逻辑提取为无装饰函数,再由缓存版本调用)。
总结:对绝大多数单元测试场景,cached_func.func(...) 是跳过 joblib 缓存最直接、最健壮的方式。它让测试回归本质——验证业务逻辑,而非缓存机制。










