Make Simple Mocks With SimpleNamespace

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. 😎
If your Django project’s long test runs bore you, I wrote a book that can help.
One summary email a week, no spam, I pinky promise.
Related posts:
Tags: python