Python中测试对象方法调用:是否有比Java式更简便的方案?
Python单元测试:验证对象方法调用的更简便方案
我有丰富的Java单元测试经验,现在转做Python开发,需要测试对象A的方法是否调用了对象B的方法。在Java里我需要给A的构造函数传入B的mock或spy对象,想问问Python是不是必须用同样的方式?或者因为Python组件隔离更宽松,有没有更简便的方案?
下面是我用Java思路实现的测试代码(已经修改了ObjA的构造函数来适配测试),想再确认下有没有更简单的方法验证ObjB::add被ObjA调用。
现有代码
obj_a.py
from obj_b import * class ObjA: def __init__(self, *args): if len(args) > 0: self.objb = args[0] # 用于测试 return self.objb = ObjB() def methodCallsB(self, x, y): return self.objb.add(x, y)
obj_b.py
class ObjB: def add(self, x, y): return x + y
test.py
import unittest from unittest.mock import patch, Mock from obj_a import * from obj_b import * class TTest(unittest.TestCase): @patch("obj_b.ObjB") def test_shouldCallBThroughA(self, mockB): # 配置mock mockB.add = Mock(return_value=137) obja = ObjA(mockB) # 调用测试方法 res = obja.methodCallsB(4, 7) print("result: " + str(res)) # 验证结果 self.assertEqual(137, res) mockB.add.assert_called_once() args = mockB.add.call_args[0] # Python 3.7 print("args: " + str(args)) self.assertEqual((4, 7), args) if __name__ =='__main__': unittest.main()
更简便的实现方案
Python确实有更简洁的方式,不需要修改生产代码(ObjA的构造函数),直接利用unittest.mock的patch功能,替换ObjA内部创建的ObjB实例即可。
核心原因是Python的动态特性允许我们直接替换模块或类中的依赖引用,不需要像Java那样依赖构造注入来实现测试隔离。
优化后的测试代码如下:
优化后的test.py
import unittest from unittest.mock import patch, MagicMock from obj_a import ObjA class TTest(unittest.TestCase): @patch("obj_a.ObjB") # 关键:替换ObjA所在模块中引用的ObjB def test_shouldCallBThroughA(self, mock_b_cls): # 配置mock实例:当ObjA创建ObjB时,会返回这个mock实例 mock_b_instance = MagicMock() mock_b_instance.add.return_value = 137 mock_b_cls.return_value = mock_b_instance # 直接创建ObjA,不需要传入mock obja = ObjA() # 调用测试方法 res = obja.methodCallsB(4, 7) # 验证结果 self.assertEqual(137, res) # 一步验证调用次数和参数 mock_b_instance.add.assert_called_once_with(4, 7) if __name__ == '__main__': unittest.main()
方案说明
- 无需修改生产代码:保留
ObjA原有的构造函数,不用为测试添加额外参数,避免生产代码被测试逻辑污染。 - 精准替换依赖:通过
@patch("obj_a.ObjB"),我们替换的是obj_a模块中导入的ObjB类,这样当ObjA的构造函数创建ObjB()时,实际得到的是我们的mock实例。 - 更简洁的验证:使用
assert_called_once_with可以直接验证调用次数和参数,不需要手动提取call_args。
内容的提问来源于stack exchange,提问作者JohnK
相关产品推荐
相关产品推荐

