MagicMock & Spec
MagicMock is a subclass of Mock that automatically supports
magic methods like __len__, __str__, and __iter__.
spec restricts mock attributes to those of a real object.
MagicMock vs Mock
from unittest.mock import Mock, MagicMock
# Regular Mock doesn't handle magic methods
mock_list = Mock()
# len(mock_list) raises TypeError
# MagicMock handles magic methods
mock_list = MagicMock()
mock_list.__len__.return_value = 3
print(len(mock_list)) # 3
# Auto-generated magic methods
mock_list.__iter__.return_value = iter([1, 2, 3])
print(list(mock_list)) # [1, 2, 3]
Using spec
from unittest.mock import MagicMock
# spec limits attributes to those of the real class
mock_user = MagicMock(spec=User)
mock_user.name = "Alice"
# This would raise AttributeError:
# mock_user.nonexistent_attribute = "oops"
# spec with an instance
real_user = User(name="Bob", age=25)
mock_user = MagicMock(spec=real_user)
mock_user.name # works
mock_user.age # works
# mock_user.bad # AttributeError
spec_set for Strict Mocking
# spec_set rejects any attribute access not in the spec
mock_service = MagicMock(spec_set=PaymentService)
mock_service.charge # works (defined in PaymentService)
# mock_service.hack # AttributeError
# Useful for ensuring tests don't depend on mock artifacts
Practical Example
class TestUserRepository(unittest.TestCase):
def test_save_user(self):
mock_session = MagicMock(spec=Session)
repo = UserRepository(mock_session)
user = User(name="Alice")
repo.save(user)
mock_session.add.assert_called_once_with(user)
mock_session.commit.assert_called_once()
Key Takeaway
Use MagicMock when you need magic method support. Use spec to
restrict mock attributes to match a real object, catching typos and ensuring your tests
interact with the correct interface.