π§ͺ Python Mocking & Stubbing β Simulate and Control Code Behavior in Tests
π§² Introduction β Why Use Mocking & Stubbing?
In unit testing, you often need to test a piece of code in isolation. But what if that code:
- Connects to a database?
- Makes HTTP calls?
- Depends on slow, external services?
This is where mocking and stubbing come in.
With Pythonβs built-in unittest.mock module, you can:
- Replace real objects with controlled stand-ins
- Simulate return values, exceptions, and side effects
- Verify how functions are called
π― In this guide, you’ll learn:
- The difference between mocking and stubbing
- How to use
Mock,MagicMock, andpatch() - How to simulate return values and exceptions
- Best practices and common pitfalls
β What Are Mocking and Stubbing?
| Term | Description |
|---|---|
| Stub | A simplified version of a function that returns fixed output |
| Mock | A fake object that records calls and can simulate complex behavior |
π‘ Stubs are passive (just return data), while mocks are interactive (track usage).
π¦ Getting Started with unittest.mock
from unittest.mock import Mock
β Create a Simple Mock
mock_func = Mock()
mock_func.return_value = "Hello"
print(mock_func()) # Output: Hello
π§ Setting Return Values and Side Effects
mock = Mock()
mock.get_data.return_value = [1, 2, 3]
mock.raise_error.side_effect = ValueError("Invalid data")
print(mock.get_data()) # [1, 2, 3]
mock.raise_error() # Raises ValueError
π MagicMock β Smarter Mocks with Built-in Magic Methods
from unittest.mock import MagicMock
m = MagicMock()
m.__len__.return_value = 5
print(len(m)) # 5
β
MagicMock is a subclass of Mock with all dunder methods included.
π§ͺ Using patch() β Replace Objects Temporarily
β Replace a Method During Test
from unittest.mock import patch
def fetch_user():
return "Real user data"
with patch('__main__.fetch_user', return_value="Mocked data"):
print(fetch_user()) # Mocked data
print(fetch_user()) # Real user data
β
patch() is perfect for mocking external dependencies, APIs, and class methods.
π οΈ Patching Objects Inside Classes
class EmailService:
def send(self):
return "Email sent"
with patch.object(EmailService, 'send', return_value="Mocked email"):
service = EmailService()
print(service.send()) # Mocked email
π§Ύ Verifying Calls and Arguments
β
assert_called_with()
mock = Mock()
mock("Alice", 42)
mock.assert_called_with("Alice", 42)
β
call_args, call_count, call_args_list
mock = Mock()
mock(1)
mock(2)
print(mock.call_count) # 2
print(mock.call_args_list) # [call(1), call(2)]
β Useful for verifying that functions were used correctly.
π§° Real-World Use Case β Mocking API Call
import requests
from unittest.mock import patch
def get_data():
response = requests.get("https://api.example.com/data")
return response.json()
@patch("requests.get")
def test_get_data(mock_get):
mock_get.return_value.json.return_value = {"key": "value"}
result = get_data()
assert result == {"key": "value"}
π‘ No actual API request is madeβpure unit testing!
π§ Mocking vs Stubbing β Summary
| Feature | Stub | Mock |
|---|---|---|
| Returns fixed data | β Yes | β Yes |
| Tracks usage | β No | β
Yes (assert_called_with) |
| Raises exceptions | β Yes | β
Yes (side_effect) |
| Best for | Data setup | Behavior verification |
π Best Practices
| β Do This | β Avoid This |
|---|---|
Use patch() as a context manager or decorator | Forgetting to stop the patch manually |
Verify arguments with assert_called_with() | Assuming mocks are always called |
Use side_effect for exceptions or sequences | Hardcoding return values only |
| Keep mocks focused on one behavior | Over-mocking everything |
π Summary β Recap & Next Steps
Python’s unittest.mock gives you the power to test any part of your code in isolationβwithout relying on databases, APIs, or third-party services.
π Key Takeaways:
- β Use stubs for predictable outputs
- β Use mocks to track function calls and simulate behavior
- β
patch()helps temporarily replace modules, classes, or functions - β
Use
MagicMockwhen mocking magic methods (__len__,__getitem__, etc.)
βοΈ Real-World Relevance:
Used in unit testing, CI/CD pipelines, test-driven development, and mock API testing.
β FAQ β Python Mocking & Stubbing
β What is the difference between Mock and MagicMock?
β
MagicMock includes default implementations for all magic methods like __str__, __len__, __getitem__, etc.
β Should I mock everything in a test?
β No. Only mock external dependenciesβkeep tests readable and relevant.
β Can I simulate exceptions with mocks?
β
Yes, using side_effect:
mock.side_effect = ValueError("Boom!")
β How do I test if a function was called?
β Use:
mock.assert_called()
mock.assert_called_with(args)
β Does mocking slow down tests?
β Noβit makes tests faster by removing external I/O.
Share Now :
