Sometimes when I develop by TDD (Test Driven Development) I need to test the calling order of some class methods. In general I write Python code so I'll show the last test case that I have just written.
I have a class with 3 methods (in the file mount_manager.py):
class MountManager:
def create_file(self):
...
def format_file(self):
...
def create_and_format(self):
self.create_file()
self.format_file()
The method create_and_format() is the code under test; in it the method create_file() must be called before the method format_file() (otherwise the calling of the method format_file() fail because the file to format doesn't exist).
With the TDD in mind, before to write the production code, I have written the test code test_create_and_format() with the goal to verify that the order of the methods called by create_and_format() would be correct:
import unittest
from unittest import mock
from mount_manager import MountManager
class MountManagerTest(unittest.TestCase):
..
def test_create_and_format(self):
mock_mount_manager = mock.create_autospec(MountManager)
MountManager.create_and_format(mock_mount_manager)
self.assertEqual('create_file', mock_mount_manager.method_calls[0][0])
self.assertEqual('format_file', mock_mount_manager.method_calls[1][0])
I know this post which is near to mine, but it doesn't work with a class and its methods but with a module and its functions; this difference stops me to apply that method to write my test code.
Because in the test I call the method under test, create_and_test(), from the class (see the instruction MountManager.create_and_format() that is a class attribute reference) and not from an instance of the class while in the production code I call the method from an instance of the class, I'm looking for a different way to write the test code.
Could someone suggest me an other way to write the test code test_create_and_format()
To test the call order on an instance you can use the attach_mock feature. Calls to an attached mock will be recorded in the method_calls and mock_calls attributes of the parent mock. Then, since they're recorded on the same mock, you can explicitly make an assertion on the call order.
import unittest
from unittest import mock
from mount_manager import MountManager
class MountManagerTest(unittest.TestCase):
def test_create_and_format(self):
mount_manager = MountManager()
parent_mock = mock.MagicMock()
with mock.patch.object(MountManager, "create_file") as mock_create:
with mock.patch.object(MountManager, "format_file") as mock_format:
parent_mock.attach_mock(mock_create, "creator")
parent_mock.attach_mock(mock_format, "formatter")
mount_manager.create_and_format()
parent_mock.assert_has_calls([mock.call.creator(), mock.call.formatter()])
This test should pass, given the mount_manager.py shown in the question.
However, if the order of create_file and format_file are switched in the implementation (i.e. in mount_manager.py) then the test should fail with an explanation looking something like this:
FAILED test_mount_manager.py::MountManagerTest::test_create_and_format - AssertionError: Calls not found.
Expected: [call.creator(), call.formatter()]
Actual: [call.formatter(), call.creator()]