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

如何在Pytest中为类的__call__魔术方法创建Spy且不修改业务代码?

解决Pytest中为__call__魔术方法创建Spy且不修改业务代码的方案

可以实现,核心是利用unittest.mock(或pytest-mock的mocker fixture)正确包装或patch目标实例/类的__call__方法,无需改动业务代码。以下是具体实现方案:

业务代码示例

假设你的业务代码结构如下:

class A:
    def __call__(self, x):
        return x * 2

class B:
    def __init__(self):
        self.a = A()
    
    def apply_a(self, value):
        # 隐式调用A实例的__call__方法
        return self.a(value)

方案1:Spy目标实例的绑定__call__方法

直接针对B实例中已初始化的A对象,为其绑定的__call__方法创建Spy,保留原功能同时记录调用:

def test_apply_a(mocker):
    b = B()
    # 对b.a的__call__方法创建Spy
    spy_call = mocker.spy(b.a, '__call__')
    
    # 执行被测方法
    result = b.apply_a(5)
    
    # 验证业务逻辑正确性
    assert result == 10
    # 验证__call__方法被正确调用
    spy_call.assert_called_once_with(5)

方案2:Patch类的__call__方法并保留原功能

如果需要在A实例创建前就设置Spy,可以patch A类的__call__方法,通过side_effect保留原逻辑:

def test_apply_a(mocker):
    # 保存A类原__call__方法
    original_call = A.__call__
    # Patch A类的__call__,保留原功能并记录调用
    mock_call = mocker.patch.object(A, '__call__', side_effect=original_call)
    
    b = B()
    result = b.apply_a(5)
    
    assert result == 10
    # 注意:类的__call__是实例方法,第一个参数为实例自身b.a
    mock_call.assert_called_once_with(b.a, 5)

原理说明

Python中隐式调用instance()时,本质是触发instance.__call__()。上述方案要么直接包装实例的绑定__call__方法,要么通过patch类方法并保留原逻辑,都能覆盖隐式调用场景,无需修改业务代码中的调用方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:52:15