When testing Python code you may need a mock object. That’s okay! But what’s the best way to build a simple mock?
Mock and various subclasses (
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 : from unittest import mock In : m = mock.Mock(verbose=False) In : if m.vrebose: ...: print("The medium is the massage") ...: The medium is the massage
For simple mocks, I prefer to use Python’s
types.SimpleNamespace. This class sets its attributes from given keyword arguments:
In : from types import SimpleNamespace In : obj = SimpleNamespace(x=12, y=17) In : obj Out: namespace(x=12, y=17) In : obj.x Out: 12 In : obj.y Out: 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
mock.patch() and co. by passing it as the
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. 😎
My new book Boost Your Django DX came out this past Monday, January 10th.
One summary email a week, no spam, I pinky promise.