使用unittest.mock上下文管理器时,如何正确调用assert_called_once?
问题根源与解决办法
核心问题:Mock的目标路径错误
你遇到的AttributeError本质是你以为替换成Mock对象的函数,实际上还是原函数——原生Python函数本身没有assert_called_once()方法,自然会报错。
最常见的原因是:你mock的是get_score_json的定义模块,而非被测试函数calc_scores实际调用它的模块。
举个实际场景例子:
假设你的项目结构如下:
my_project/ ├── score_utils.py # 定义get_score_json的文件 └── calculator.py # 包含calc_scores的文件,里面用from score_utils import get_score_json导入并调用
当calculator.py执行from score_utils import get_score_json时,会把get_score_json的引用复制到calculator模块的命名空间里。如果你测试时mock的是score_utils.get_score_json,根本不会影响calculator模块里已经存在的那个函数引用——calc_scores还是在调用原函数,不是Mock对象。
正确写法示例
被测试代码(calculator.py)
from score_utils import get_score_json def calc_scores(): score_data = get_score_json() return sum(score_data.values())
测试代码(test_calculator.py)
from unittest.mock import patch from calculator import calc_scores def test_calc_scores(): # 关键:mock的是calculator模块里的get_score_json,而非score_utils里的原函数 with patch('calculator.get_score_json') as mock_get_score: # 设置Mock的返回值 mock_get_score.return_value = {'math': 92, 'chinese': 88} # 执行被测试函数 total = calc_scores() # 现在mock_get_score是标准Mock对象,支持assert_called_once() mock_get_score.assert_called_once() # 验证计算结果 assert total == 180
其他可能的坑
- 不要用
new参数替换普通函数:如果你给patch传了new=lambda: {}这类普通函数,替换后的对象是普通函数,不是Mock对象,自然没有断言方法。 - 不要在上下文管理器外调用断言:
patch的上下文结束后,Mock对象会被还原成原函数,这时调用assert_called_once()同样会触发错误。
内容的提问来源于stack exchange,提问作者ssloan
相关产品推荐
相关产品推荐

