What is mock

Python mock module allows you to replace parts of your system under test with mock objects and make assertions about how they have been used.

Mock is available in the standard library in python3 as part of unittest module. You can read it's documentation here. In python 2, mock is available to install with pip.

Use cases

What do we use mocks for? Mock are mostly used in unit tests. We try to make tests as short and focused on what we test as possible.

Mock call to external service

As I stated in the first paragraph, mock is mainly used for testing. Recently I was implementing a social signup and I didn't want to call facebook every time, but I wanted to test if the signup works as it's supposed to.

Calling an external service such as facebook is really expensive, because http requests take time (hundreds of milliseconds at least) and requires thinking about lots of complex things - such as if your access token is still valid. And we usually don't want to think about it during testing, because we want to test this one specific detail of our code.

Below is a short gist of this usage of mock:

get_json_mock = mock.Mock(return_value={'name': 'John',
                                        'email': 'john@doe.com'})

class SocialAuthViewTest(APITestCase):
    backend = "facebook"

    def setUp(self):
        test_token = 'token test_facebook'
        self.url = reverse('authenticate_social', args=(self.backend,))
        self.client.defaults['HTTP_X_AUTHORIZATION'] = test_token

    @mock.patch('social.backends.base.BaseAuth.get_json', get_json_mock)
    def test_returns_200_when_user_found(self):
        response = self.client.post(self.url)
        self.assertEqual(response.status_code, 200)

If we know which function is called and what it should return, we can patch it using @mock.patch decorator and previously created Mock object with return_value set to what we want to get as result of function call.

Locally turn off logging

Let's imagine that we've already implemented most of our social login functionality and now we want to test if our API properly reacts to bad logging in attacks.

We don't want user to see ugly 500 - internal server error HTTP error code, but 401 unauthorized HTTP error code.

When we get information from an external service that credentials were invalid we should log the accident. In tests our logger prints to standard out. But do we want an ugly traceback in our tests output? I would rather see something like this:

Ran 17 tests in 1.610s


Short and clean. But how should I keep loggers quiet?

I quickly searched the web to find good solutions to this problem. Stack overflow provides some answers but they weren't satisfactory to me, because most of them included changing logging too much, by changing it's level globally in tests.

I wanted a more fine grained approach and I used mock library for it.

First I had to find the the file logging the exception and understand how it's done.

Here is a file users/views.py

logger = logging.getLogger(__name__)

def sign_in_or_signup(register, access_token):
        return request.backend.do_auth(access_token)
    except requests.HTTPError:
        logger.exception('Invalid login')

It's a pretty standard django way to use logging. We create logger instance for the module and use it to print out messages to standard out or send details of errors to some external service such as sentry.

If we only could mock this logger to swallow exceptions...


    @mock.patch('users.views.logger', mock.Mock())
    def test_user_authenticates_wrong_access_token(self):
        self.client.defaults['HTTP_X_AUTHORIZATION'] = 'token iamjustwrong'
        response = self.client.post(self.url)
        self.assertEquals(response.status_code, 401)

VoilĂ , done! It's even easier than the previous example.

Testing a behavior flow

Another interesting usage of mock can be while testing check and retry if wrong pattern.

My coworker was testing this kind of behavior and used code similar to the one below:

def generate_value_mock(*args, **kwargs):
    iterator = getattr(generate_value_mock, 'iterator', 0)
    setattr(generate_value_mock, 'iterator', iterator + 1)

    if iterator < 2:
        return 'a'
    return 'b'

Nice piece of advanced python, right? Actually it demands thinking, it's complex. Maybe it even should be tested to be sure that it's behavior is correct. One of the testing rules is that we don't test tests, so they should be as simple as possible.

Guess what, we can use mock library to simplify this piece of code. Below is the gist from IPython representing how it works:

m = mock.Mock()
In [17]: m.test.side_effect = ['a', 'a', 'b']

In [18]: m.test()
Out[18]: 'a'

In [19]: m.test()
Out[19]: 'a'

In [20]: m.test()
Out[20]: 'b'

We can set side_effect on Mock object. Depending on what type of object we pass to it, we can get different behaviors when calling mock object.

Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT, the return value of this function is used as the return value. Alternatively side_effect can be an exception class or instance. In this case the exception will be raised when the mock is called. If side_effect is an iterable then each call to the mock will return the next value from the iterable. A side_effect can be cleared by setting it to None.

You can read more about this particular property here.

It's only a tip of the iceberg...

If you haven't heard about mock before, you might find this examples impressive, however they don't show even 10% of real capabilities of python mock library.

I recommending you reading it's detailed documentation or if you prefer more hands on approach, you can check out mock tutorial form Toptal.

Anyway, happy mocking!