15

I'm writing a class that orchestrates two instruments (a remote controllable power supply unit and a bus controller used to control the Device-Under-Test) in order to perform all kinds of measurements on a Device-Under-Test (DUT).

Access to both instruments is implemented as Python classes and a reference to each is available to the new class. The DUT is somewhat delicate and has a very specific power-up sequence involving calls to the power supply and bus controller and has to happen in that specific order to avoid damaging the DUT.

Now I want to write a unit-test for this class. I am currently using nosetests and the mock-package for this. So my idea was to mock both instrument classes and verify the correct call order for those.

It seems to be very easy to verify the call order for each mocked class itself. Hence, I am able to find out if the power supply was ordered to correctly apply battery voltage first, then digital domain voltage and then analog domain voltage. I can also find out that the digital registers have been programmed to the correct values. However, I'm stuck determining if the call to write the digital registers happened between applying the digital domain voltage and analog domain voltage.

So my question is: If I have two mocked objects, how can I verify a specific order of calls between those objects? My first though was to check timestamps of the calls but those don't seem to exist.

jan
  • 927
  • 2
  • 9
  • 20
  • Can you check the following are true in order: neither A nor B is called; A is called but B isn't; A and B are called? – jonrsharpe Apr 20 '15 at 13:31
  • 1
    i think you can use side_effect on Mock to kick-off another function that will report whatever you want to report - i.e. store something in a global list or somewhere convenient – user3012759 Apr 20 '15 at 13:33
  • 1
    @user3012759 Same thing I was thinking; you might even be able to , e.g., use the side effect of A to assert that B has not been called. – chepner Apr 20 '15 at 13:34
  • If I don't change the code for the sake of testing I can only do things before and after the whole sequence. So I cannot hold in the middle and check if certain calls have been done already and others haven't yet. I'll look into using side effects to achieve what I need. – jan Apr 20 '15 at 13:46

1 Answers1

20

What you can do is put your mocks in a new Mock() object and check the mock's calls of the new mock. Maybe an example is simpler to understand:

>>> from unittest.mock import *
>>> m0, m1 = Mock(), Mock()
>>> m = Mock()
>>> m.m0, m.m1 = m0, m1
>>> m0()
<Mock name='mock.m0()' id='140660445334224'>
>>> m1()
<Mock name='mock.m1()' id='140660445334608'>
>>> m.mock_calls
[call.m0(), call.m1()]

Ok now we are in good position: we can just check the calls of m to verify the correct order:

>>> m.assert_has_calls([call.m0(), call.m1()])
>>> m.assert_has_calls([call.m1(), call.m0()])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/mock.py", line 863, in assert_has_calls
    'Actual: %r' % (calls, self.mock_calls)
AssertionError: Calls not found.
Expected: [call.m1(), call.m0()]
Actual: [call.m0(), call.m1()]

As we want the first pass and the inverse order fail.

When you use patch simply take the mocks returned by the patches and put them in a new Mock() as container. Do your check on the container that record the child calls order.

Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
  • Aye, that works perfectly. Thanks. Just for those interested, autospec works on the sub-mocks as well. – jan Apr 20 '15 at 15:50
  • @jan Yes, once you created your mock set it as a child of another mock don't change it. – Michele d'Amico Apr 20 '15 at 15:54
  • 3
    FYI, this didn't work for me. I kept getting an empty call list. I had to use `attach_mock` to properly track the calls, see this answer: https://stackoverflow.com/a/22677452/3405140 – moertel Jun 06 '17 at 09:25
  • 1
    @moertel maybe your are trying to attach a mock that already have a parent. If you take a look to https://github.com/python/cpython/blob/3.6/Lib/unittest/mock.py#L420 you can see that `attach_mock()` simply call `setattr` after clean `mock`'s parent properties. I tested it on last `mock` release in both python 2 and 3. – Michele d'Amico Jun 06 '17 at 09:59
  • It would probably be clearer to create the parent mock `m` first and then do `m0, m1 = m.m0, m.m1`. This would avoid the confusion with attaching mocks. – C S Jul 09 '18 at 14:07
  • @CS Most of the times you already have the mocks and you need just check the order. – Michele d'Amico Jul 09 '18 at 14:41