如何用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
关键说明
- 正确设置返回值:
mock_get是MyOuterClass.get方法的模拟对象,需通过return_value指定它调用后返回的模拟MyInnerClass实例。 - 直接赋值属性:模拟内部类的属性是普通字段,直接赋值字符串即可,
side_effect仅适用于模拟函数/方法的多次调用或异常抛出。 - 修正断言语句:原代码中
assert mock_get.get.called_once()错误,正确断言应为mock_get.assert_called_once(),因为mock_get本身就是被模拟的get方法。
内容的提问来源于stack exchange,提问作者ApJo
相关产品推荐
相关产品推荐

