2014-03-27 59 views
19

我有三個功能,我試圖測試呼叫次序。檢查多個嘲笑的呼叫次序

比方說,在模塊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之後被稱爲以下,和c之前。所以得到一個模擬的每一個,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 

檢查,每個individial嘲笑的被稱爲是很容易。我如何查看相互之間的通話順序?

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 

但這只是給了我,嘲笑,被稱爲與ARGS,但不是實際的模擬呼叫作出反對。我可以添加多一點的邏輯:

# 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中已經有東西可以做到這一點,我錯過了。

回答

20

定義一個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()功能添加看到它拋出AssertionError

查看更多的例子在Tracking order of calls and less verbose call assertions

希望有所幫助。

+1

一個重要的通知 - 不要在補丁(...)中設置autospec = True。如果你設置autospec = True attach_mock,請不要正確輸入。 您的示例中沒有autospec,但它通常存在於現實生活中的測試用例中。 –