You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

方案说明

  1. 无需修改生产代码:保留ObjA原有的构造函数,不用为测试添加额外参数,避免生产代码被测试逻辑污染。
  2. 精准替换依赖:通过@patch("obj_a.ObjB"),我们替换的是obj_a模块中导入的ObjB类,这样当ObjA的构造函数创建ObjB()时,实际得到的是我们的mock实例。
  3. 更简洁的验证:使用assert_called_once_with可以直接验证调用次数和参数,不需要手动提取call_args。

内容的提问来源于stack exchange,提问作者JohnK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 11:40:48