使用pytest assert_has_calls测试函数调用顺序报错求助
问题:assert_has_calls断言失败,函数调用顺序测试不通过
要测试main_func内部子函数的调用顺序,用pytest和unittest.mock写了测试用例,但运行时assert_has_calls断言失败,预期调用与实际调用不匹配,求解决方法或替代实现方案。
业务代码
def get_data(): pass def process_data(data): pass def notify_admin(action): pass def save_data(data): pass def main_func(): notify_admin('start') data = get_data() processed_data = process_data(data) save_data(processed_data) notify_admin('finish')
测试代码
import pytest from unittest.mock import patch, Mock, call from main_func import main_func @patch('main_func.notify_admin') @patch('main_func.get_data') @patch('main_func.process_data') @patch('main_func.save_data') def test_main_func(mock_4, mock_3, mock_2, mock_1): execution_order = [mock_1, mock_2, mock_3, mock_4] order_mock = Mock() for order, mock in enumerate(execution_order): order_mock.attach_mock(mock, f'f_{order}') main_func() order_mock.assert_has_calls([ call.f_1(), call.f_2(), call.f_3(), call.f_4(), call.f_1(), ])
错误信息
E AssertionError: Calls not found. E Expected: [call.f_1(), call.f_2(), call.f_3(), call.f_4(), call.f_1()] E Actual: [call.f_1('start'), E call.f_2(), E call.f_3(<MagicMock name='mock.f_3()' id='2049968460848'>), E call.f_4(<MagicMock name='mock.f_2()' id='2049968489424'>), E call.f_1('finish')]
问题原因
- 参数不匹配:预期调用未包含实际调用的参数(
notify_admin传了'start'/'finish',process_data和save_data接收了上游mock的返回值) - 命名对应错误:
enumerate从0开始计数,导致f_0对应notify_admin,但预期里误用了f_1,命名与实际mock完全不匹配 - patch顺序混淆:unittest.mock装饰器是从下往上绑定参数的,用序号命名mock容易搞混对应关系
解决方法
方案一:修正参数匹配与mock命名(推荐)
用ANY忽略无需关注的参数,给mock起清晰的业务名称,避免序号混淆:
import pytest from unittest.mock import patch, Mock, call, ANY from main_func import main_func @patch('main_func.save_data') @patch('main_func.process_data') @patch('main_func.get_data') @patch('main_func.notify_admin') def test_main_func(mock_notify, mock_get, mock_process, mock_save): # 创建调用顺序跟踪mock,给每个子函数mock起明确名称 order_tracker = Mock() order_tracker.attach_mock(mock_notify, 'notify') order_tracker.attach_mock(mock_get, 'get') order_tracker.attach_mock(mock_process, 'process') order_tracker.attach_mock(mock_save, 'save') main_func() # 验证调用顺序和必要参数,用ANY忽略process/save的具体参数值 order_tracker.assert_has_calls([ call.notify('start'), call.get(), call.process(ANY), call.save(ANY), call.notify('finish') ])
方案二:直接验证mock调用顺序(无需attach_mock)
如果觉得attach_mock麻烦,可以手动检查每个mock的调用时机:
import pytest from unittest.mock import patch, call, ANY from main_func import main_func @patch('main_func.save_data') @patch('main_func.process_data') @patch('main_func.get_data') @patch('main_func.notify_admin') def test_main_func(mock_notify, mock_get, mock_process, mock_save): main_func() # 验证调用顺序:通过mock调用记录的时间戳比较先后 assert mock_notify.call_args_list[0] < mock_get.call_args_list[0] assert mock_get.call_args_list[0] < mock_process.call_args_list[0] assert mock_process.call_args_list[0] < mock_save.call_args_list[0] assert mock_save.call_args_list[0] < mock_notify.call_args_list[1] # 验证参数正确性 assert mock_notify.call_args_list == [call('start'), call('finish')] assert mock_process.call_args == call(mock_get.return_value) assert mock_save.call_args == call(mock_process.return_value)
内容的提问来源于stack exchange,提问作者venv
相关产品推荐
相关产品推荐

