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

如何用MagicMock模拟内部类属性以完成Python单元测试?

问题

项目mymodule目录下有my_module_1.py和my_module_2.py两个模块:

  • my_module_1的MyOuterClass包含内部类MyInnerClass,其get方法调用AWS服务后返回MyInnerClass实例;
  • my_module_2的MyModule2类中get_foo方法调用该get方法,通过内部类属性生成字符串。

使用pytest fixture结合mocker.patch.object模拟MyOuterClass的get方法后,断言时返回的是MagicMock对象而非预期值,导致断言失败,错误信息显示字段值为MagicMock实例。需解决如何正确模拟内部类属性以通过断言。


相关代码

my_module_1.py

class MyOuterClass(object):
    class MyInnerClass(object):
        attribute1: str
        attribute2: str
        attribute3: str
    def get(self) -> MyInnerClass:
        ''' 
        伪代码
         1. 调用AWS服务
         2. 用返回值设置内部类属性
         3. 返回内部类实例
        '''

my_module_2.py

class MyModule2():
    def get_foo(self, some_boolean_predicate):
        if some_boolean_predicate:
            temp = my_module_1.MyOuterClass().get()
            statement = f'''
            WITH (
                BAR (
                    FIELD_1 = '{temp.attribute1}',
                    FIELD_2 = '{temp.attribute2}',
                    FIELD_3 = '{temp.attribute3}'
                )
            )
            '''
        else:
            statement = ''
        return statement

测试代码(test_my_module2.py)

@pytest.fixture
def mock_get(mocker: MockerFixture) -> MagicMock:
    return mocker.patch.object(MyOuterClass, 'get')

def test_should_get_from_inner_class(mock_get):
        output = mock_get.get
        output.attribute1.side_effect = 'attr1'
        output.attribute2.side_effect = 'attr2'
        output.attribute3.side_effect = 'attr3'
        mock_output_str = '''
            WITH (
                BAR (
                    FIELD_1 = 'attr1',
                    FIELD_2 = 'attr2',
                    FIELD_3 = 'attr3'
                )
            )
            '''
        module2Obj = MyModule2()
        response = module2Obj.get_foo(some_boolean_predicate=True)
        assert mock_get.get.called_once()
        assert response == mock_output_str

错误信息

FAILED [100%]
            WITH (
                BAR (
                    FIELD_1 = '<MagicMock name='get().attribute1' id='4937943120'>',
                    FIELD_2 = '<MagicMock name='get().attribute2' id='4937962976'>',
                    FIELD_3 = '<MagicMock name='get().attribute3' id='4937982928'>'
                )
            )

解决方法

问题核心是模拟逻辑错误:未正确设置get方法的返回值,且错误操作了不存在的mock_get.get属性。正确做法是让get方法返回模拟的MyInnerClass实例,并直接给实例属性赋值。

修正后的测试代码

@pytest.fixture
def mock_get(mocker: MockerFixture) -> MagicMock:
    return mocker.patch.object(MyOuterClass, 'get')

def test_should_get_from_inner_class(mock_get):
    # 创建模拟的MyInnerClass实例
    mock_inner = mocker.Mock()
    mock_inner.attribute1 = 'attr1'
    mock_inner.attribute2 = 'attr2'
    mock_inner.attribute3 = 'attr3'
    
    # 设置get方法返回该模拟实例
    mock_get.return_value = mock_inner
    
    mock_output_str = '''
            WITH (
                BAR (
                    FIELD_1 = 'attr1',
                    FIELD_2 = 'attr2',
                    FIELD_3 = 'attr3'
                )
            )
            '''
    module2Obj = MyModule2()
    response = module2Obj.get_foo(some_boolean_predicate=True)
    
    # 断言get方法被调用一次
    mock_get.assert_called_once()
    assert response == mock_output_str

关键说明

  1. 正确设置返回值:mock_get是MyOuterClass.get方法的模拟对象,需通过return_value指定它调用后返回的模拟MyInnerClass实例。
  2. 直接赋值属性:模拟内部类的属性是普通字段,直接赋值字符串即可,side_effect仅适用于模拟函数/方法的多次调用或异常抛出。
  3. 修正断言语句:原代码中assert mock_get.get.called_once()错误,正确断言应为mock_get.assert_called_once(),因为mock_get本身就是被模拟的get方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 03:27:39