14

Suppose I have a python function

def func(self):
    self.method_1()
    self.method_2()

How can I write an unit test that can assert method_1 is called before method_2?

@mock.patch(method_1)
@mock.patch(method_2)
def test_call_order(method_2_mock, method_1_mock):
     # Test the order
Kintarō
  • 2,947
  • 10
  • 48
  • 75
  • 1
    I think you're missing the word *"before"*... Have you read e.g. http://www.voidspace.org.uk/python/mock/examples.html#tracking-order-of-calls-and-less-verbose-call-assertions? – jonrsharpe Sep 08 '15 at 17:05

4 Answers4

19

Your case is a slight variation of Python Unit Testing with two mock objects, how to verify call-order?. What you should do is set method_2_mock, method_1_mock as children of one new mock object and then ask for mock_calls attribute or use assert_has_calls:

@mock.patch(method_1)
@mock.patch(method_2)
def test_call_order(method_2_mock, method_1_mock):
    mock_parent = Mock()
    mock_parent.m1, mock_parent.m2 = method_1_mock, method_2_mock
    <test code>
    """Check if method 1 is called before method 2"""
    mock_parent.assert_has_calls([call.m1(), call.m2()])

There are lot of details omitted in this code like call arguments. Take a look to call and the very useful ANY helper.

ATTENTION This is valid just for unitetest.mock in python3. For python 2.7 and mock 1.0.1 you should use attach_mock instead.

Community
  • 1
  • 1
Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
2

Another option is to create a simple list and append each mock to it through side_effect.

@mock.patch(method_1)
@mock.patch(method_2)
def test_call_order(method_2_mock, method_1_mock):
    call_order = []
    method_1_mock.side_effect = lambda *a, **kw: call_order.append(method_1_mock)
    method_2_mock.side_effect = lambda *a, **kw: call_order.append(method_2_mock)
    # Run test code...
    assert call_order == [method_1_mock, method_2_mock]

Each time the method is called, the side_effect lambda function is called. Since lists are ordered, this is a clean way to check the call order of your methods.

Chris Collett
  • 1,074
  • 10
  • 15
0

Improving on the second approach, by Chris Collett.

Using side_effect like that makes the call return None. This is a problem if you need the mock method to return a value. A simple solution is to use a helper function:

call_order = []

def log_ret(func, ret_val):
    call_order.append(func)
    return ret_val

method_1_mock.side_effect = lambda *a, **kw: log_ret(method_1_mock, 'return_value_1')
method_2_mock.side_effect = lambda *a, **kw: log_ret(method_2_mock, 'return_value_2')

Cheers

saza
  • 460
  • 6
  • 7
0

For me Michele d'Amico's answer does not work.

I have found the following way to in doc https://docs.python.org/3/library/unittest.mock-examples.html#tracking-order-of-calls-and-less-verbose-call-assertions

@mock.patch(method_1)
@mock.patch(method_2)
def test_call_order(method_2_mock, method_1_mock):
    manager = Mock()
    manager.attach_mock(method_1_mock, 'method_1_mock')
    manager.attach_mock(method_2_mock, 'method_2_mock')

    #call func()
    
    """Check if method 1 is called before method 2"""
    manager.assert_has_calls([call.method_1_mock(), call.method_2_mock()])
Ivan Rostovsky
  • 614
  • 1
  • 8
  • 16