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

如何基于不可调用的spec创建可调用的mock对象?

解决方案

问题根源

  1. 用One实例作为autospec参数时,mock会将One视为不可调用的实例对象,而非可生成实例的类,因此抛出NonCallableMagicMock不可调用的错误。
  2. 仅用autospec=True时,默认生成的mock实例允许手动添加或访问原类不存在的属性,无法检测到One属性变更但Wrapper未同步的问题。

正确实现方式

要同时满足「mock类可调用生成实例」和「严格校验实例属性的存在性」,可通过以下两种方法实现:

方法一:使用create_autospec创建严格mock

create_autospec可以基于原类生成严格遵循其结构的mock,结合spec_set=True禁止添加原类不存在的属性:

from unittest import mock
import pytest

class One:
    def __init__(self, a):
        self.a = a

class Wrapper:
    def __init__(self, wa):
        self._one = One(a=wa)
        self.wa = self._one.a

def test_wrapper():
    # 创建严格遵循One类结构的mock,spec_set=True限制属性必须存在于原类
    MockOne = mock.create_autospec(One, spec_set=True)
    test_a = 'bar'
    # 设置mock实例的a属性(仅当原类存在a时可操作)
    MockOne.return_value.a = test_a

    # 替换原One类
    with mock.patch(__name__ + '.One', MockOne):
        wrapper_obj = Wrapper(wa=test_a)

        MockOne.assert_called_once_with(a=test_a)
        assert wrapper_obj.wa == test_a

方法二:结合autospec=True与spec_set限制实例属性

在patch时启用autospec=True,同时为mock的返回实例设置spec_set,严格校验实例属性:

from unittest import mock
import pytest

class One:
    def __init__(self, a):
        self.a = a

class Wrapper:
    def __init__(self, wa):
        self._one = One(a=wa)
        self.wa = self._one.a

@mock.patch(__name__ + '.One', autospec=True)
def test_wrapper(MockOne):
    test_a = 'bar'
    # 创建严格遵循One实例结构的mock对象,禁止添加不存在的属性
    mock_instance = mock.Mock(spec_set=One(a=test_a))
    mock_instance.a = test_a
    MockOne.return_value = mock_instance

    wrapper_obj = Wrapper(wa=test_a)

    MockOne.assert_called_once_with(a=test_a)
    assert wrapper_obj.wa == test_a

验证属性变更检测

当One的属性被修改(比如将self.a改为self.b),而Wrapper仍保留self.wa = self._one.a时,测试会抛出AttributeError: Mock object has no attribute 'a',从而检测到Wrapper未同步更新的问题,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 02:01:14