Make Simple Mocks With SimpleNamespace

Wild mock objects appeared!

When testing Python code you may need a mock object. That’s okay! But what’s the best way to build a simple mock?

unittest.mock provides Mock and various subclasses (NoncallableMock, MagicMock, NoncallableMagicMock). These classes are useful and general purpose, but they’re also fairly complex. You have to pick between them, decide on the various arguments they take, and they come with several “gotchas”. Notably, unless you pass in spec, typo’d attribute access will return a new Mock, which is truthy:

In [1]: from unittest import mock

In [2]: m = mock.Mock(verbose=False)

In [3]: if m.vrebose:
    ...:     print("The medium is the massage")
    ...:
The medium is the massage

Uh-oh!

For simple mocks, I prefer to use Python’s types.SimpleNamespace. This class sets its attributes from given keyword arguments:

In [1]: from types import SimpleNamespace

In [2]: obj = SimpleNamespace(x=12, y=17)

In [3]: obj
Out[3]: namespace(x=12, y=17)

In [4]: obj.x
Out[4]: 12

In [5]: obj.y
Out[5]: 17

It’s as simple as possible, with no faff around being callable, tracking usage, etc.

You can use a SimpleNamespace to replace an object when you know only certain attributes are required:

from types import SimpleNamespace

import example


def test_should_log():
    config = SimpleNamespace(verbose=True)

    result = example.should_log(config)

    assert result is True

You can also use SimpleNamespace with mock.patch() and co. by passing it as the new argument:

from types import SimpleNamespace
from unittest import mock

import example


def test_should_log():
    config = SimpleNamespace(verbose=True)

    with mock.patch.object(example, "config", config):
        example.log("Hello world")

    # Assert message logged
    ...

Great stuff. 😎

Fin

May your mocks be as simple as possible,

—Adam


If your Django project’s long test runs bore you, I wrote a book that can help.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: