How to mock in Python? – (almost) definitive guide

What is a mock?

Mock is a category of so-called test doubles – objects that mimic the behaviour of other objects. They are meant to be used in tests to replace real implementation that for some reason cannot be used (.e.g because they cause side effects, like transferring funds or launching nukes). Mocks are used to write assertions about the way they are used – e.g. if they were called, which arguments were used etc. It is a flagship technique of interaction-based testing – checking how objects under test use their collaborators (other objects).

That being said, Python mocks can also be used as stubs – another type of test double that is meant to return canned (hardcoded before) responses. Then, we write assertions about state or value returned from an object under test.

As you can see, we make no assertions about stubbed dependency. Whether we should use mocks or stubs is a whole different story and is beyond the scope of this article.

Note that I used literal 1 for order_id – this could also be classified as test double, but this time called dummy – a value just passed to exercise an object under test but without real influence on the outcome.

The most important takeaway from this part is that mock is used to mimic other objects and we make assertions about how it was used.

Mock – simple examples

To create a mock one needs to instantiate unittest.mock.Mock class. By default, Mock will automagically create missing attributes on-the-fly that will be Mocks. If we call such an attribute, we’re also gonna get Mock.

No matter how cool and magic it looks like, it is rather undesired behaviour in tests. Remember that Mocks are to replace real objects. In order to bring any value, they have to behave like them. Reliance on attribute auto-creation is a misuse of mocks and leads to false-positive tests.

The same goes for returned value – we should not let Mocks leak into further calls. To control it, we use return_value keyword argument:

assert_* methods of Mock (+ unsafe parameter)

Mock instances have a bunch of helpful methods that can be used to write assertions. For example, we can easily assert if mock was called at all: mock.assert_called() or if that happened with specific arguments: assert_called_once_with(argument='bazinga')

Before Python 3.5 that feature in combination with dynamic attributes creation could be very dangerous. If we made a typo in assert_* method name, mock would just happily create a Mock instance on the fly. As a result, we won’t even have an assertion (another false-positive test, yay):

Such a mistake was difficult to spot, but luckily Python3.5 introduced a new keyword to Mock – unsafe with False as default. This makes Mock raise an AttributeError if we try to call a not-existing method starting with “assert” or “assret”:

Even though this can be seen as a backwards-incompatible change in Python, it definitely was a good one.

Can I make assertions if a real object was called (not a mock)? – wraps

If for whatever reason you decide to not replace your real object in the test but still want to make assertions if it was used, you can leverage wraps parameter:

Raising exception from Mock (side_effect)

To make our Mock raise an exception upon call, we use keyword argument side_effect:

The rule is simple – if a value passed as side_effect is an Exception class or instance, it will get raised. However, it turns out side_effect has more interesting applications…

Returning a few different values from Mock, raising different exceptions

It may happen we will be calling the same Mock several times and we need it to return different values for each call or raise an Exception but only on third call. To achieve this, we pass a list (or another sequence) as side_effect:

It is not all yet – side_effect can also accept a callable. It will be called and its result (or exception) visible as if it is coming from a mock:

If we combine this capability with classes defining __call__ method, we can mimic any behaviour we want with side_effect:

If a mock accepts arguments, we need to introduce arguments to __call__.

How to safely mock a class? How to safely mock a function?

It is not hard to create a Mock that can be used in a test, but making it safe and reliable is a very different story. If you think seriously about using mocks, you have to get to know spec and spec_set keyword arguments. They both change the undesired default behaviour of Mock class (creating attributes on-the-fly). Both spec and spec_set accept a class/function you want to mimic. spec will raise AttributeError if you try to access an attribute that is not defined on the class while still letting you set non-existent attributes manually. spec_set forbids that and raises AttributeError.

spec_set should be your default choice.

How to get even safer mocks with sealing?

Now, even though spec_set seems to be good enough safety measure, it is not always sufficient. Assume there is a class with two or more similar methods and we mock only one of them for the test:

Now, if we mock one method (foo in the above example), but an object under test uses unmocked method bar, it will still return a Mock instance.

If we are lucky, our test will fail and we will quickly mock the right method. If it is not our lucky day, we are gonna get false-positive (a passing test for the wrong implementation). I fell into such a situation a few years back. In the end, we created a factory method around Mock called “safe_mock” that will raise an exception if you tried to use an unmocked method. There is no need to do that manually today because Python3.7 has remedy included – sealing mocks:

You should also always seal all your mocks for extra security.

Mocking versus patching

We know how to make mocks useful and it all looks fine, but all previous examples showed code where we could easily pass a Mock instance to an object under test. In reality, we (too) often deal with code that looks like this:

foobar instance is an implicit dependency of tested_function. We still may need to replace it in the test, but now there is no simple way to do so.

Luckily, Python has our back and it comes with unittest.mock.patch. I could show examples of how to do patching but despite years of Python experience I still sometimes get them wrong :(. A colleague of mine showed me an alternative – patch.object – that is much easier to get right:

A recipe is simple – we pass a module where our object under test comes from as the first argument of patch.object and name (string!) of an object being mocked as a second argument.

patch as well as patch.object both can be used as decorators or context managers:

However, consider patching as your last resort for code you do not control. It should be done sparingly. The real problem is that tested_function is untestable. To make it easier you should rather consider refactoring your code to use explicit composition and dependency injection via __init__ arguments:

How do I mock or patch requests? How to mock time/datetime? How to mock another popular library?

If you’re struggling to patch a third party library you have no control over, chances are someone has already done it and created a library to help you with that.

For an excellent library for making HTTP calls – requests – a test utility called responses (I admire that name) has been created.

Mocking time/datetime, doing some time-travel, eh? Look no more, use freezegun.

Async Mocks – an honourable mention

If we write asynchronous code, we may need at some point a possibility to conveniently mock coroutines. Since Python3.8, there is AsyncMock class in standard library. If you’re stuck with older python, you can use one of several available libraries.

Summary

Knowing how to write mocks is one thing, but testing effectively is a completely different story. It is beyond the scope of this article, but you will see articles on that matter on that blog as well. 🙂 However, being proficient with Mocks is already a huge step forward. Do not try to patch/hack your code to be able to test – rather make it easily testable.

Always remember about using spec_set and sealing your mocks!

9

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.