6

I would like to unittest a fuction and assert if the sequence of function calls made inside the function workflow(). Something like,

      [1st called] fetch_yeargroup_ls()
      [2nd called] invoke_get_links()....... 

I searched across many discussions but never found one answering my question.

Madhan Gokul
  • 173
  • 1
  • 8

2 Answers2

6

If you are using mock you can create mocks as attributes of a parent mock, when patching out those functions:

try:
    # Python 3
    from unittest.mock import MagicMock, patch, call
except ImportError:
    # Python 2, install from PyPI first
    from mock import MagicMock, patch, call
import unittest

from module_under_test import function_under_test

class TestCallOrder(unittest.TestCase):
    def test_call_order(self):
        source_mock = MagicMock()
        with patch('module_under_test.function1', source_mock.function1), \
                patch('module_under_test.function2', source_mock.function2), \
                patch('module_under_test.function3', source_mock.function3)

            # the test is successful if the 3 functions are called in this
            # specific order with these specific arguments:
            expected = [
                call.function1('foo'),
                call.function2('bar'),
                call.function3('baz')
            ]

            # run your code-under-test
            function_under_test()

            self.assertEqual(source_mock.mock_calls, expected)

Because the 3 functions are attached to source_mock, all calls to them are recorded on the parent mock object in the Mock.mock_calls attribute and you can make assertions about their call order.

I attached the 3 function mocks simply by looking them up as attributes on the source_mock object, but you could also use the Mock.attach_mock() method to attach mocks you created in a different way to a parent.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks Martijn Pieters, this is what I was looking for (to be specific). I was not exposed to the concept of MagicMock() and it sounds good to explore. – Madhan Gokul Aug 10 '15 at 09:24
1

In my opinion, the real purpose of testing or specifically TDD is to have better designed code. If writing tests for your code is becoming difficult, then it signifies that your code is highly coupled and you need to refactor it. Besides, writing tests that assert the order of the functions, make tests too coupled to the code and highly fragile.

With that said, you can test the order, in a little hackish way though. Say, you have two functions named func_a and func_b, and you want to ensure func_a is called before func_b. Replace func_a with a mock that returns some random value such that it stops execution from happening any further. Mock func_b as well, and ensure func_a is called and func_b is not called. You need to do this for other permutations as well.

hspandher
  • 15,934
  • 2
  • 32
  • 45
  • To be frank this is my third assignment is Unittesting and 1st time using mocking. So I will consider your point and learn to write a good designed code. Thanks for the little hackish :-) – Madhan Gokul Aug 10 '15 at 09:27
  • I love that everyone in SO says order shouldn't matter for tests, but in fact that is a good use case. For example api calls to add a service, another api call to start it, and another api call to check status. Making all these 3 into one huge tests also seems like a bad idea. You can't start it before adding the service. – thefern Feb 10 '23 at 20:34