Mock
MagicMock
Python testing
unittest
Python mock library

Mock vs MagicMock

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Mock and MagicMock are classes in Python's unittest.mock library for creating test doubles. MagicMock is a subclass of Mock that automatically provides default implementations for all magic (dunder) methods like __len__, __iter__, __getitem__, and __str__. Use MagicMock by default because it works in more situations. Use Mock when you need to prevent magic method calls from silently succeeding (e.g., to catch accidental use of len() or iteration on a mock).

Mock Basics

Mock creates an object that records all calls and attribute accesses:

python
1from unittest.mock import Mock
2
3# Create a mock
4service = Mock()
5
6# Call any method — automatically created
7service.get_user(42)
8service.save_data("hello", key="test")
9
10# Assert calls were made
11service.get_user.assert_called_once_with(42)
12service.save_data.assert_called_with("hello", key="test")
13
14# Configure return values
15service.get_user.return_value = {"id": 42, "name": "Alice"}
16result = service.get_user(42)
17print(result)  # {"id": 42, "name": "Alice"}
18
19# Access call history
20print(service.get_user.call_count)  # 2
21print(service.get_user.call_args_list)

MagicMock Extras

MagicMock adds automatic support for magic methods:

python
1from unittest.mock import Mock, MagicMock
2
3# Mock does NOT support magic methods by default
4regular = Mock()
5# len(regular)  # TypeError: object of type 'Mock' has no len()
6# for item in regular: pass  # TypeError: 'Mock' object is not iterable
7
8# MagicMock supports magic methods automatically
9magic = MagicMock()
10print(len(magic))        # 0 (default __len__ returns 0)
11print(bool(magic))       # True (default __bool__ returns True)
12print(str(magic))        # Mock name/id string
13print(int(magic))        # 1 (default __int__ returns 1)
14print(magic[0])          # MagicMock (default __getitem__)
15print(list(magic))       # [] (default __iter__ returns empty iterator)

Key Differences

python
1from unittest.mock import Mock, MagicMock
2
3mock = Mock()
4magic = MagicMock()
5
6# __len__
7try:
8    len(mock)   # TypeError!
9except TypeError:
10    print("Mock has no __len__")
11
12print(len(magic))  # 0 — works
13
14# __iter__
15try:
16    list(mock)  # TypeError!
17except TypeError:
18    print("Mock is not iterable")
19
20print(list(magic))  # [] — works
21
22# __getitem__
23try:
24    mock[0]  # TypeError!
25except TypeError:
26    print("Mock has no __getitem__")
27
28print(magic[0])  # MagicMock — works
29
30# __contains__
31try:
32    "x" in mock  # TypeError!
33except TypeError:
34    print("Mock does not support 'in'")
35
36print("x" in magic)  # False — works

When to Use Each

Use MagicMock (Default Choice)

python
1from unittest.mock import MagicMock
2
3# Mocking objects that will be iterated, compared, or used with len()
4mock_db = MagicMock()
5mock_db.__iter__.return_value = iter([{"id": 1}, {"id": 2}])
6
7for row in mock_db:
8    print(row)  # Works because __iter__ is supported
9
10# Mocking context managers
11mock_file = MagicMock()
12mock_file.__enter__.return_value = mock_file
13mock_file.read.return_value = "file content"
14
15with mock_file as f:
16    print(f.read())  # "file content"

Use Mock (When You Want Strictness)

python
1from unittest.mock import Mock
2
3# Use Mock when magic method usage would indicate a bug
4api_client = Mock()
5
6# If your code accidentally tries to iterate the API client,
7# Mock raises TypeError immediately — catching the bug
8# MagicMock would silently return an empty list

Configuring Magic Methods

python
1from unittest.mock import MagicMock
2
3# Configure __len__
4container = MagicMock()
5container.__len__.return_value = 5
6print(len(container))  # 5
7
8# Configure __getitem__
9data = MagicMock()
10data.__getitem__.side_effect = lambda key: f"value_{key}"
11print(data["name"])   # "value_name"
12print(data[42])       # "value_42"
13
14# Configure __iter__
15items = MagicMock()
16items.__iter__.return_value = iter(["a", "b", "c"])
17print(list(items))  # ['a', 'b', 'c']
18
19# Configure __contains__
20collection = MagicMock()
21collection.__contains__.return_value = True
22print("anything" in collection)  # True
23
24# Configure __bool__
25flag = MagicMock()
26flag.__bool__.return_value = False
27if not flag:
28    print("Flag is falsy")  # This prints

Common Patterns

Mocking a Database Repository

python
1from unittest.mock import MagicMock
2
3def get_user_names(repository):
4    users = repository.find_all()
5    return [user.name for user in users]
6
7# Create mock users
8mock_user1 = MagicMock()
9mock_user1.name = "Alice"
10mock_user2 = MagicMock()
11mock_user2.name = "Bob"
12
13# Create mock repository
14repo = MagicMock()
15repo.find_all.return_value = [mock_user1, mock_user2]
16
17result = get_user_names(repo)
18print(result)  # ['Alice', 'Bob']
19repo.find_all.assert_called_once()

Using spec for Safety

python
1from unittest.mock import MagicMock
2
3class UserService:
4    def get_user(self, user_id: int):
5        pass
6
7    def save_user(self, user):
8        pass
9
10# spec restricts the mock to only have methods defined on UserService
11mock_service = MagicMock(spec=UserService)
12
13mock_service.get_user(42)        # OK
14mock_service.save_user("Alice")  # OK
15# mock_service.delete_user(42)   # AttributeError! delete_user doesn't exist

side_effect for Dynamic Returns

python
1from unittest.mock import MagicMock
2
3api = MagicMock()
4
5# Return different values on successive calls
6api.fetch.side_effect = [{"page": 1}, {"page": 2}, StopIteration]
7
8print(api.fetch())  # {"page": 1}
9print(api.fetch())  # {"page": 2}
10
11# Raise an exception
12api.connect.side_effect = ConnectionError("timeout")
13# api.connect()  # Raises ConnectionError
14
15# Dynamic behavior based on arguments
16def lookup(user_id):
17    return {"id": user_id, "name": f"User {user_id}"}
18
19api.get_user.side_effect = lookup
20print(api.get_user(1))   # {"id": 1, "name": "User 1"}
21print(api.get_user(42))  # {"id": 42, "name": "User 42"}

Common Pitfalls

  • Using Mock when the code uses len(), in, or iteration: Mock raises TypeError for magic methods. If the code under test calls len(mock) or iterates over the mock, use MagicMock instead.
  • Forgetting that MagicMock silently accepts all magic method calls: MagicMock.__len__() returns 0 by default instead of raising an error. This can mask bugs where magic methods are called unexpectedly. Use Mock with spec for stricter behavior.
  • Not using spec to restrict mock attributes: Without spec, both Mock and MagicMock accept any attribute access or method call. Typos like mock.gett_user() silently succeed. Use spec=RealClass to catch these errors.
  • Confusing return_value with side_effect: return_value sets a static return value. side_effect can be a function, an iterable of values, or an exception. Setting both means side_effect takes precedence.
  • Asserting calls in the wrong order: assert_called_with() only checks the most recent call. Use assert_any_call() to check if a call was made at any point, or call_args_list to verify the complete call sequence.

Summary

  • MagicMock extends Mock with default implementations for all magic methods (__len__, __iter__, __getitem__, etc.)
  • Use MagicMock by default — it works in more situations and is the standard choice
  • Use Mock when you want magic method calls to raise TypeError (stricter testing)
  • Add spec=RealClass to either class to restrict the mock to only the real class's interface
  • Configure magic methods with mock.__len__.return_value = 5 for custom behavior

Course illustration
Course illustration

All Rights Reserved.