Labs ICT
โญ Pro Login

MagicMock & Spec

Using MagicMock and spec for realistic mocks

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.

๐Ÿงช Quick Quiz

MagicMock is a subclass of Mock that: