检查多个模拟的调用顺序

2022-01-08 00:00:00 python function mocking python-mock

问题描述

我正在尝试测试三个函数的调用顺序.

I have three functions that I'm trying to test the call order of.

假设在模块 module.py 我有以下内容

Let's say that in module module.py I have the following

# 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 中的每一个进行模拟很容易:

I want to check that b is called after a, and before c. So getting a mock for each of a, b and c is easy:

# 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

检查每个单独的模拟是否被调用也很容易.如何检查通话相对于彼此的顺序?

Checking that each of the individial mocks are called is easy, too. How do I check the order of the calls relative to one another?

call_args_list 不起作用,因为它是为每个模拟单独维护的.

call_args_list won't work as it's maintained separately for each mock.

我尝试使用副作用来记录每个调用:

I've tried using a side effect to log each of the calls:

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

但这只会给我调用模拟的参数,而不是调用所针对的实际模拟.我可以添加更多逻辑:

But this only gives me the args that the mocks were called with, but not the actual mock that the call was made against. I can add a bit more logic:

# 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 中应该已经有一些东西可以做到这一点,而我却错过了.

And that seems to get the job done... Is there a better way though? It feels like there should already be something in the API that can do this that I'm missing.


解决方案

定义一个 Mock 管理器并通过 attach_mock().然后检查 mock_calls:

Define a Mock manager and attach mocks to it via attach_mock(). Then check for the 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() 函数中的函数调用顺序,添加查看它是否抛出 AssertionError.

Just to test that it works, change the order of function calls in the main_routine() function add see that it throws AssertionError.

在 跟踪调用顺序和不那么冗长的调用断言

希望对您有所帮助.

相关文章