如何在Python单元测试中Mock cachetools缓存值?自定义装饰器Mock失败排查
如何在Python单元测试中Mock cachetools缓存值(解决自定义装饰器Mock失败问题)
先直接说你的测试失败的核心原因,再给你两种可行的解决方案:
问题根源
你的测试代码犯了两个关键错误:
- Mock对象的用法错误:
total_cache是一个已经初始化好的TTLCache实例,不是可调用的函数/类,所以设置mock_total_cache.return_value = self.fake_cache完全无效——return_value只对可调用对象(比如类、函数)生效,对于已存在的实例,这个操作不会改变它的任何行为。 - 装饰器的绑定时机问题:
cache_controller装饰器在ExampleClass定义时,就已经把原total_cache实例的引用绑定到了装饰器内部的闭包中。哪怕你后来Mock了src.run.total_cache,装饰器里用的还是原来那个空的TTLCache实例。
解决方案一:重新绑定装饰器的缓存实例
既然装饰器已经绑定了原缓存,我们可以直接替换测试类中方法使用的缓存,重新给目标方法应用装饰器并传入假缓存:
import unittest from cachetools.ttl import TTLCache from src.run import ExampleClass from unittest import TestCase from src.wrappers.cache_controller import cache_controller class TestCacheController(TestCase): def setUp(self): self.fake_cache = TTLCache(maxsize=10, ttl=3600) self.fake_cache[(5, 6)] = 1 # 直接给假缓存设置预期值 self._class = ExampleClass() # 拿到原函数(__wrapped__是functools.wraps保留的原函数引用) original_method = self._class.example_method.__wrapped__ # 重新用假缓存应用装饰器,替换掉原来的方法 self._class.example_method = cache_controller( cache=self.fake_cache, cache_args=("a", "b") )(original_method) def tearDown(self): self._class = None def test_if_cache_filled(self): result = self._class.example_method(a=5, b=6) cache_expected = 1 print("Result:", result) self.assertEqual(result, cache_expected)
解决方案二:直接Mock缓存实例的方法
如果你不想修改装饰器的绑定逻辑,可以直接Mock原缓存实例的get和update方法,控制它们的返回值和行为:
import unittest from src.run import ExampleClass from unittest import TestCase, mock class TestCacheController(TestCase): def setUp(self): self._class = ExampleClass() @mock.patch("src.run.total_cache") def test_if_cache_filled(self, mock_total_cache): # 设置get方法返回我们预期的缓存值 mock_total_cache.get.return_value = 1 result = self._class.example_method(a=5, b=6) cache_expected = 1 print("Result:", result) self.assertEqual(result, cache_expected) # 还可以验证缓存方法的调用是否符合预期 mock_total_cache.get.assert_called_once_with((5, 6)) # 因为缓存命中,update方法不应该被调用 mock_total_cache.update.assert_not_called()
额外优化建议
如果后续需要频繁测试这个装饰器,建议把缓存的注入方式改得更灵活,比如让类支持传入自定义缓存:
# 修改src/run.py from .wrappers.cache_controller import cache_controller from .helpers.cache import total_cache class ExampleClass(object): def __init__(self, cache=None): # 优先使用传入的缓存,没有则用默认的total_cache self._cache = cache or total_cache # 修改装饰器,让它从实例中获取缓存 @cache_controller( cache=lambda self: self._cache, cache_args=("a", "b") ) def example_method(self, a: int, b: int): return a+b
当然这需要你调整cache_controller的逻辑,让它支持接收可调用对象来获取缓存实例,这样测试时直接传入假缓存会更简洁。
内容的提问来源于stack exchange,提问作者sumeyyeemir
相关产品推荐
相关产品推荐

