我有三个函数,我正在尝试测试它的调用顺序.
假设在模块module.py中我有以下内容
# module.py def a(*args): # do the first thing def b(*args): # do a second thing def c(*args): # do a third thing def main_routine(): a_args = ('a') b_args = ('b') c_args = ('c') a(*a_args) b(*b_args) c(*c_args)
我想检查b在a之后和c之前被调用.因此,对a,b和c中的每一个进行模拟很容易:
# tests.py @mock.patch('module.a') @mock.patch('module.b') @mock.patch('module.c') def test_main_routine(c_mock, b_mock, a_mock): # test all the things here
检查每个单独的模拟被调用也很容易.如何检查呼叫相对于彼此的顺序?
call_args_list
不会工作,因为它是为每个模拟单独维护.
我尝试使用副作用来记录每个调用:
calls = [] def register_call(*args): calls.append(mock.call(*args)) return mock.DEFAULT a_mock.side_effect = register_call b_mock.side_effect = register_call c_mock.side_effect = register_call
但是这只能给我一些嘲笑的诅咒,而不是实际的嘲笑.我可以添加更多逻辑:
# tests.py from functools import partial def register_call(*args, **kwargs): calls.append(kwargs.pop('caller', None), mock.call(*args, **kwargs)) return mock.DEFAULT a_mock.side_effect = partial(register_call, caller='a') b_mock.side_effect = partial(register_call, caller='b') c_mock.side_effect = partial(register_call, caller='c')
而这似乎完成了工作......虽然有更好的方法吗?感觉应该已经在API中已经有一些东西可以做到这一点,我错过了.
定义一个Mock
管理器并通过它来附加模拟attach_mock()
.然后检查mock_calls
:
@patch('module.a') @patch('module.b') @patch('module.c') def test_main_routine(c, b, a): manager = Mock() manager.attach_mock(a, 'a') manager.attach_mock(b, 'b') manager.attach_mock(c, 'c') module.main_routine() expected_calls = [call.a('a'), call.b('b'), call.c('c')] assert manager.mock_calls == expected_calls
只是为了测试它是否有效,在main_routine()
函数add中更改函数调用的顺序,看它是否抛出AssertionError
.
请参阅跟踪调用顺序和更简洁的调用断言的更多示例
希望有所帮助.
我今天需要这个答案,但是问题中的示例代码确实很难阅读,因为调用args与管理器上以及测试范围内的模拟名称相同。这是有关此概念的官方文档,下面是非机器人的更清晰示例。为了示例,我正在修补的所有模块都是:
@patch('module.file_reader') @patch('module.json_parser') @patch('module.calculator') def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader): manager = Mock() # First argument is the mock to attach to the manager. # Second is the name for the field on the manager that holds the mock. manager.attach_mock(mock_file_reader, 'the_mock_file_reader') manager.attach_mock(mock_json_parser, 'the_mock_json_parser') manager.attach_mock(mock_calculator, 'the_mock_calculator') module.main_routine() expected_calls = [ call.the_mock_file_reader('some file'), call.the_mock_json_parser('some json'), call.the_mock_calculator(1, 2) ] assert manager.mock_calls == expected_calls
请注意,attach_mock
在这种情况下您必须使用,因为模拟是由创建的patch
。patch
要通过attach_mock
该代码工作,必须附加具有名称(包括由创建的名称)的模拟项。您没有使用attach_mock
,如果你让你自己Mock
没有名称的对象:
def test_main_routine(mock_calculator, mock_json_parser, mock_file_reader): manager = Mock() mock_file_reader = Mock() mock_json_parser = Mock() mock_calculator = Mock() manager.the_mock_file_reader = mock_file_reader manager.the_mock_json_parser = mock_json_parser manager.the_mock_calculator = mock_calculator module.main_routine() expected_calls = [ call.the_mock_file_reader('some file'), call.the_mock_json_parser('some json'), call.the_mock_calculator(1, 2) ] assert manager.mock_calls == expected_calls