Testing Python with Mockito

November 10, 2020
mockito python unit-testing

A significant percentage of writing software is invoking other bits of software that may or may not be in your control. That sounds straightforward, after all most of this code doesn’t even really “do” anything. But as with most things in life, mistakes are made and thus small bugs find their way into your code.

Testing is the obvious answer to this problem. Now before you climb your high horse and start pouring out the bucket of arguments on how good coders don’t need to write tests–and definitely not for simple code–, how testing takes too much time, that your boss won’t let you- or whatever other bad excuse (copied-from-some-Quora-answer-you-may-or-may-not-have-found-on-Google) you can bring up; here is why I think unit testing is valuable:

If your code will be maintained by others in the future, they will value your tests for the same reasons, but in reverse order.

import unittest

def two_plus_two():
    return 2 + 2

class SimpleUnitTest(unittest.TestCase):

    def test_two_plus_two(self):
        self.assertEqual(4, two_plus_two())

Testing “active” code is easy. 2 + 2 should always be 4. Your thermostat should turn on the heat when the temperature drops below X, etcetera. All unit test frameworks have great support for this “transactional” kind of testing, but when all you’re trying to do is verify if something calls something else correctly, test code can become quite unwieldy.

from unittest import TestCase
from unittest.mock import patch
import imaginary_math_library

def two_plus_two():
    return imaginary_math_library.add(2, 2)

class SimpleUnitTestMockTest(TestCase):

    def test_two_plus_two(self):
        with patch.object(imaginary_math_library, 'add', \
            return_value=4) as mock_add:

            # Test outcome
            self.assertEqual(4, two_plus_two())

        # ensure 'add' was called correctly
        mock_add.assert_called_once_with(2, 2)

Python’s unittest framework offers the mock library, which lets you define fake methods that can verify wether they were called as intended. In the example above, patch.object temporarily overwrites the add method on imaginary_math_library with a mock that always returns 4. When two_plus_two() is called in the test, it will invoke the mock add method.

The mock library works great, there are even some nice touches like the patch context manager–used above–that are a great improvement over building your own mock objects and patching them into code under test. However, when you get serious with it, things become verbose in a hurry. The reason for this verbosity is that it separates mock definition from call verification. Furthermore, mocks have to be “manually” injected or patched into code under test.

I have fond memories from back in the day when I wrote Java (a language normally regarded as one that re-defines the word verbose) of using a framework that offered a quick and simple way of applying mocks in unit testing, Mockito. Mockito uses a builder-pattern-like chained-method syntax that combines mock definition and installation. Verification is still done through separate calls, but despite that, tests written with Mockito are concise and readable.

One can only imagine my surprise and delight when, while looking around for ways to test Python code earlier this year, I stumbled upon mockito-python, a Python implementation of the aforementioned Java library.

Mockito-python (Mockito hereafter) takes a host of great features from it’s Java namesake and throws in a few extra to make things even better. Not only does it combine mock installation and definition in the same statements, expectations get set up simultaneously. This means that calls with unexpected parameters automatically cause test failures. Using Mockito, our little example is implemented this way:

from unittest import TestCase
from mockito import when, verifyStubbedInvocationsAreUsed
import imaginary_math_library

def two_plus_two():
    return imaginary_math_library.add(2, 2)

class SimpleMockitoTest(TestCase):

    def test_two_plus_two(self):
        when(imaginary_math_library).add(2, 2).thenReturn(4)
        self.assertEqual(4, two_plus_two())
        verifyStubbedInvocationsAreUsed()

verifyStubbedInvocationsAreUsed will ensure that all mocks defined in a test are used. This is a feature that is absent from the Java library. It only needs to be called once, in contrast to explicit call verification which has to happen for every expected call in a test.

Depending on the nature of your test suite, verifyStubbedInvocationsAreUsed can be invoked from a tearDown() method. This is also where you should invoke mockito.unstub() which undoes all changes applied by Mockito, to prevent the next test from being surprised by side-effects.

Mockito offers a whole host of argument Matchers that can be used to describe parameter expectations and you can use an argument Captor to inspect parameters even further. Mocked methods can return fixed values, raise errors and even execute lambdas if dynamic results are needed in your test.

The whole thing is very well documented and a joy to use. I’ve written a slew of test code with Mockito over the course of this year. Doing so has helped me find a bunch of silly bugs and most of all has helped me build confidence in my code-even the trivial parts!

Since discovering Mockito for Python, my proverbial coal mine is packed to the brim with canaries and I love it! I hope it’s useful to you too. If that is the case, or if you have suggestions, drop me line. Enjoy!

-@niels

Test-object builders for Stripe API resources

November 12, 2020
Working with the Stripe API is unlike most anything else in the world of software development. Stripe’s documentation is outstanding. Their API is intuitive and the language specific clients are easy to use. Each Stripe account comes with a test environment that makes development and integration testing effortless. The stripe command line tool can be used for simple API queries, setting up authentication and testing of web hooks. On top of all that, there is stripe-mock, a local service with a near- complete implementation of the API that can be used for testing and development without requiring a network connection to the Stripe Test environment...

mockito python stripe unit-testing