Mock objects

unittest · Advanced · 12 min

Mocks isolate the unit under test from slow, flaky or costly dependencies such as APIs and databases.

unittest.mock.Mock creates an object that accepts any attribute access or call, records everything, and returns more mocks. You configure what it does and then assert how it was used.

Configuring:

  • m.return_value = 42: what calling it returns.
  • m.side_effect = ...: an exception to raise, an iterable of successive return values, or a function to call with the same arguments.
  • Attributes: m.get_user.return_value = {"id": 1} configures a method on the mock.
  • Mock(name="api") improves repr in failure messages.

Asserting:

  • m.assert_called_once_with(1, key="x"), m.assert_called_with(...) (last call), m.assert_any_call(...), m.assert_not_called().
  • m.call_count, m.call_args, m.call_args_list, m.mock_calls for detailed inspection.

MagicMock also supports magic methods (len(m), iteration, with m:), with sensible defaults. patch creates MagicMocks by default.

Mocks accept anything, including typos: m.asert_called_once() would silently do nothing, except that Mock raises AttributeError for unknown names starting with assert, assret and similar. Specs, covered later, close the general hole.

Example

from unittest.mock import Mock, call

def notify_overdue(api, user_ids):
    sent = 0
    for uid in user_ids:
        user = api.get_user(uid)
        if user["overdue"]:
            api.send_email(user["email"], subject="Payment overdue")
            sent += 1
    return sent

api = Mock(name="api")
api.get_user.side_effect = [
    {"email": "a@x.io", "overdue": True},
    {"email": "b@x.io", "overdue": False},
]
print(notify_overdue(api, [1, 2]))                 # 1
api.send_email.assert_called_once_with("a@x.io", subject="Payment overdue")
print(api.get_user.call_args_list == [call(1), call(2)])   # True

api.get_user.side_effect = ConnectionError("down")
try:
    notify_overdue(api, [3])
except ConnectionError as e:
    print("raised:", e)                            # raised: down

Remember

  • Mocks record calls; configure them with return_value and side_effect
  • assert_called_once_with and call_args_list verify interactions
  • MagicMock adds support for magic methods

Watch out for

  • Asserting on a misspelled method of an unspecced mock; the attribute is created and nothing is checked.

Check yourself

  1. m.side_effect = [1, 2] and m() is called three times. What happens on the third call?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/unittest.mock.html#the-mock-class