Unrecognized Escape Sequence

An increasingly grumpy blog about software engineering

Unit testing in Python with dependency injection

A foreword on this approach to unit testing in Python

Python is a very flexible language, and while the creators of Python tried to follow the guiding principle “there should be one, and preferably only one, obvious way to do things”, this flexibility inevitably means that there are – in fact – lots of ways to do things, some good, some bad.

Unit testing in Python is no exception to this. One of these flexibilties arises from the fact that Python is a dynamically-typed language in which meta-programming and reflection are trivial and commonly used. As a result, you can get up and running with your unit test as simply as, for example, re-assigning a class member to a stub or mock as and when required. Moreover, as Python has no enforced member access modifiers (e.g. strictly public or private functions), one of the classic problems of unit testing, summarised by the question “How do I test a private method?”, can be answered as simply as “just call it”.

However this can lead to problems arising from the old trap of forgetting what unit tests are for. In many cases unit testing is as much about maintaining code modularity than it is about verifying the behaviour of code. The length of rope that Python gives you to make unit testing “easier” is more than enough to hang yourself at the alter of the latter, while totally ignoring the former.

Although I’ve written quite a lot of Python, I wouldn’t call myself an expert on the language. When I started writing production code in Python I followed an approach to unit-testing based on dependency injection, as one would in a strongly-typed language such as C++. In some ways this was more force of habit under time pressure than a reasoned decision, but in time I came to realise the benefits of sticking to this pattern as the project grew in complexity. To my eyes, this approach – while being quite verbose and possibly non-“pythonic” – ensures that code modularity is still actively encouraged (if not enforced) by the practice of consciously breaking out explicit dependencies.

All this is not to say that the aforementioned features of the language aren’t extremely useful when it comes to unit testing, and in certain circumstances we are empowered to grease the cogs when we need to. Compared with static-typed, older-generation languages, as long as one takes care to establish an approach to avoid some of the pitfalls, writing unit tests in Python can be clean and easy.

Setting up a test case

This example is based on a MVC-style controller class used in an software update application. This controller contains some of the business logic of the application, for example determining whether a check for software updates can and should be made, based on whether there’s an internet connection available, and when the last check as performed.

To get started, here’s the test case setUp method:

import unittest
from unittest.mock import patch, call, Mock
from update_controller import UpdateController


class UpdateControllerTestCase(unittest.TestCase):

    def setUp(self):

        # Mock objects and methods

        self.mock_logger = Mock()
        self.mock_network_model = Mock()
        self.mock_update_model = Mock()
        self.mock_view_manager = Mock()

        # Create the object under test

        self.update_controller = UpdateController(
            self.mock_logger,
            self.mock_update_model,
            self.mock_network_model,
            self.mock_view_manager,
        )

The first thing to mention here is how simple it is to create the mocks. We import the in-built Mock class, create our mock dependencies using it, and inject them into the controller instance being tested.

That’s it.

Having done a lot of unit testing in C++, the first time I saw this my mind was blown.

But it gets better. Let’s look a our first test, which can be described as: “If an internet connection is available, but we’ve already checked for updates today, we don’t check again."


    def test_checks_for_updates_only_if_not_checked_so_far_today(self):

        # Setup

        self.mock_network_model.check_for_internet_connection.return_value = True
        self.mock_update_model.has_checked_today.return_value = True

        # Run

        self.update_controller.start()

        # Test

        self.mock_update_model.check_for_updates.assert_not_called()

So the first thing:

mock_network_model.check_for_internet_connection.return_value = True

How freaking incredible is that? There’s nothing hidden here, we simply tell the framework that there’s a method called check_for_internet_connection on the network model, and if and when it is called, it should return True. The magic of Python.

Asserting the behaviour of the controller is equally simple:

mock_update_model.check_for_updates.assert_not_called()

Let’s look at a slightly more complicated example. The controller contains a method called wait_or_exit which is similar to a sleep but allows the program to handle signals (for example to exit) while execution is paused. We don’t really want to do this in our tests, so as a special case, we patch this method in setUp:

    def setUp(self):

        ...

        # Special mock: don't actually sleep when wait_or_exit() is called

        self._mock_wait_method = patch.object(self.update_controller, "wait_or_exit")
        self._mock_wait_method.start()


    def tearDown(self):

        self._mock_wait_method.stop()

Now we can test as follows:

    def test_if_no_internet_checks_again_after_ten_seconds(self):

        # Setup

        self.mock_network_model.check_for_internet_connection.side_effect = [
            False,
            False,
            True,
        ]

        # Run

        self.update_controller.start()

        # Test

        self.update_controller.wait_or_exit.assert_has_calls([call(10), call(10)])

Here we are using the side_effect pattern to indicate that the first two times the check_for_internet_connection method is called on the mock it should return False, but on the third time return True.

Then we then are asserting that the wait_or_exit method was called twice (i.e. ten second delays between checks for internet connectivity).

Sure, we could have some kind of utility class passed in as another dependency to perform what is essentially a sleep, but this illustrates an alternative which in certain circumstances can be legitimate.