Python类打补丁与类方法打补丁的差异及测试问题解析
两种Python Mock方式的断言差异原因解析
先看你提供的代码示例:
class.py
class SomeClass: def __init__(self, some_value): self.some_value = some_value def add_to_value(self, add_value): self.some_value = self.some_value + add_value
function_thing.py
from my_pkg.class import SomeClass def some_function(): some_class_instance = SomeClass(5) some_class_instance.add_to_value(6)
test_function_thing.py
from my_pkg.function_thing import some_function class TestSomeFunction(TestCase): # 此测试可行,能识别方法被传入6调用 @patch("my_pkg.function_thing.SomeClass") def test_some_function_01(self, mock_some_class): instance_mock = mock_some_class.return_value some_function() instance_mock.add_to_value.assert_called_with(6) # 此测试不可行 @patch("my_pkg.function_thing.SomeClass.add_to_value") def test_some_function_02(self, mock_add_to_value): some_function() # 此断言成功 mock_add_to_value.assert_called_once() # 此断言失败,提示调用参数不是6而是某个对象 mock_add_to_value.assert_called_with(6)
为什么test_some_function_01能正常工作?
当你用@patch("my_pkg.function_thing.SomeClass")把整个类Mock掉时,SomeClass变成了一个Mock类对象:
- 当
some_function里执行SomeClass(5)创建实例,本质是调用这个Mock类的构造方法,Mock类的return_value就是它“返回”的实例Mock对象。 - 后续调用
some_class_instance.add_to_value(6),实际是调用这个实例Mock的add_to_value方法,传递的参数就是6,所以assert_called_with(6)能精准匹配。
为什么test_some_function_02的第二个断言失败?
直接Mock类的方法SomeClass.add_to_value时,你替换的是类层面的方法绑定。而Python中调用实例方法的逻辑是:
- 当你执行
some_class_instance.add_to_value(6),Python会自动把实例some_class_instance作为第一个参数(也就是方法定义里的self)传递给方法。 - 所以被Mock的
add_to_value实际接收到的调用参数是(some_class_instance, 6),而不是你预期的(6)。 - 这就是为什么
assert_called_once()能过(确实被调用了一次),但assert_called_with(6)失败——因为实际参数是实例对象加6,不是只有6。
总结
如果要断言实例方法的调用参数,最优方式是Mock整个类,然后通过类Mock的return_value获取实例Mock,再断言实例Mock的方法调用。这种方式完全隔离了原类的实现,测试更可靠。
如果非要直接Mock类方法,断言时需要把实例对象也包含进去,但这样测试会耦合实例的具体状态,灵活性差,不推荐。
内容的提问来源于stack exchange,提问作者wyt
相关产品推荐
相关产品推荐

