Python单元测试:如何Mock get_dict使其每次返回未修改字典?
问题背景
我有一个类MyClass,其中的my_func方法会调用get_dict获取字典并修改它。my_func被@retry(3)装饰,失败时会重试最多3次。这个方法要求获取的字典必须恰好包含一个键值对,否则会抛出异常。简化后的代码如下:
class MyClass: def get_dict(self): # 返回一个字典 @retry(3) # 控制错误处理和重试的装饰器 def my_func(self): a_dict = self.get_dict() if len(a_dict) == 0: raise WrongException if len(a_dict) > 1: raise OtherWrongException key, value = a_dict.popitem() # 弹出字典中的键值对 # 执行其他操作 raise MyException
遇到的问题
在对my_func的失败场景做单元测试时,我需要验证所有重试失败后是否会正确抛出MyException。我Mock了get_dict的返回值,但因为my_func里用了popitem修改字典,第一次调用后Mock的字典就空了,重试时会触发WrongException,导致测试失败。实际场景中get_dict每次调用都会返回新的字典,但当前Mock方式做不到这点。
我的测试代码如下:
import MyClass from unittest import mock, TestCase class MyTest(TestCase): @mock.patch.object(MyClass, "get_dict") def test_my_func(self, mock_dict): my_class = MyClass() mock_dict.return_value = {"key": "value"} self.assertRaises(MyException, my_class.my_func)
我试过用side_effect传入和重试次数等量的字典列表,但这不是理想方案,有没有更好的方式让Mock的get_dict每次都返回未被修改的新字典?
解决方案
你可以通过给side_effect赋值一个匿名函数(lambda),让它每次调用时都生成一个新的字典实例,而不是复用同一个字典对象:
import MyClass from unittest import mock, TestCase class MyTest(TestCase): @mock.patch.object(MyClass, "get_dict") def test_my_func(self, mock_dict): my_class = MyClass() # 每次调用get_dict都返回一个新的字典 mock_dict.side_effect = lambda: {"key": "value"} self.assertRaises(MyException, my_class.my_func)
原理说明
- 当使用
return_value时,Mock会返回同一个字典对象的引用,所以第一次popitem修改后,后续调用拿到的都是这个已经被修改的字典。 - 而
side_effect如果是一个函数,每次调用Mock方法时都会执行这个函数,并返回它的结果。这里的lambda每次执行都会创建一个新的{"key": "value"}字典,确保每次get_dict拿到的都是未被修改的全新字典,完美模拟真实场景的行为。
如果需要更复杂的字典生成逻辑,也可以把lambda换成普通函数:
def generate_new_dict(): return {"key": "value"} mock_dict.side_effect = generate_new_dict
内容的提问来源于stack exchange,提问作者btj29
相关产品推荐
相关产品推荐

