Python单元测试:如何Mock返回对象的函数?断言报AttributeError如何处理
单元测试Mock自定义返回对象的断言失效修复
报错根因
- 报错核心来源:你为
init_some_class配置的返回值,是自定义普通类MockSomeClass的实例,这个类内部定义的add_dict_to_db是普通的Python实例方法,并非unittest.mock框架提供的Mock对象,本身不具备assert_called_once_with这类断言接口。 - 当你尝试对普通函数调用Mock专属的断言方法时,就会触发
AttributeError: 'function' object has no attribute 'assert_called_once_with'的报错。 - 代码中存在的额外潜在问题:报错栈里出现的
add_participant_to_entry与业务代码实际调用的add_dict_to_db方法名不匹配,也会触发同类属性访问错误。
可落地的修复方案
方案1:将自定义Mock类的方法替换为Mock实例
在自定义Mock类的初始化方法里,把需要做调用校验的方法替换成unittest.mock.Mock对象,让方法本身拥有断言能力:
from unittest import mock import unittest from main import main class MockSomeClass: def __init__(self): # 给需要校验的方法绑定Mock实例,指定业务逻辑需要的返回值 self.add_dict_to_db = mock.Mock(return_value={}) class TestMain(unittest.TestCase): @mock.patch('main.main.init_some_class') def test_business_logic(self, mock_init_some_class): mock_some_instance = MockSomeClass() # 给被打补丁的初始化函数指定返回实例 mock_init_some_class.return_value = mock_some_instance fake_dict = {'key': 'value'} fake_credentials = {'username': 'fake_user', 'password': '1234'} main.business_logic(fake_credentials, fake_dict) # 现在可以正常对Mock方法做调用断言 mock_some_instance.add_dict_to_db.assert_called_once_with(fake_dict)
方案2:利用Mock的链式特性直接配置,无需手写自定义类
这是单元测试中更常用的写法,借助unittest.mock的自动链式生成子Mock的能力,不需要额外编写自定义Mock类:
from unittest import mock import unittest from main import main class TestMain(unittest.TestCase): @mock.patch('main.main.init_some_class') def test_business_logic(self, mock_init_some_class): # 链式配置返回值,框架会自动为链式路径上的属性、方法生成可断言的Mock实例 mock_init_some_class.return_value.add_dict_to_db.return_value = {} fake_dict = {'key': 'value'} fake_credentials = {'username': 'fake_user', 'password': '1234'} main.business_logic(fake_credentials, fake_dict) # 直接沿链式路径访问到对应方法的Mock实例,执行断言 mock_init_some_class.return_value.add_dict_to_db.assert_called_once_with(fake_dict)
注意事项
- 打补丁的路径必须匹配实际使用的命名空间:你当前的补丁路径
main.main.init_some_class的前提是,main模块通过from some_package.some_file import init_some_class将该函数引入到自身的模块命名空间下,补丁必须打在函数实际被引用、使用的位置,不能打在原定义的源文件位置,否则Mock不会生效。 - 不要重复导入不同来源的
mock模块:当前测试代码同时引入了unittest.mock和独立的mock第三方库,两个来源的Mock对象实例不互通,会引发行为异常,统一使用Python标准库自带的unittest.mock即可。 - 断言时的方法名必须和业务代码的实际调用名完全一致,避免出现拼写不匹配引发的属性访问错误。
内容的提问来源于stack exchange,提问作者Ahmed Zaki
相关产品推荐
相关产品推荐

