Mocking and Patching
Learning Objectives
- By the end of this lesson, you will be able to:
- - Understand unittest.mock module
- - Create and use mock objects
- - Use patching techniques
- - Mock functions and methods
- - Mock external dependencies
- - Use MagicMock and Mock
- - Apply mocking in tests
- - Understand when to use mocks
- - Debug mocking issues
- - Follow mocking best practices
Lesson 17.4: Mocking and Patching
Learning Objectives
By the end of this lesson, you will be able to:
- Understand unittest.mock module
- Create and use mock objects
- Use patching techniques
- Mock functions and methods
- Mock external dependencies
- Use MagicMock and Mock
- Apply mocking in tests
- Understand when to use mocks
- Debug mocking issues
- Follow mocking best practices
Introduction to Mocking
Mocking is the practice of replacing real objects with fake objects (mocks) in tests. This allows you to test code in isolation without depending on external systems.
Why Mocking?
- Isolation: Test code without external dependencies
- Speed: Avoid slow operations (network, database)
- Control: Control behavior of dependencies
- Reliability: Tests don't depend on external systems
- Focus: Test your code, not dependencies
What is Mocking?
Mocking involves replacing real objects with mock objects that simulate their behavior.
unittest.mock Module
Basic Mock
The unittest.mock module provides the Mock class:
from unittest.mock import Mock
# Create a mock object
mock_obj = Mock()
# Use the mock
mock_obj.some_method()
mock_obj.some_attribute
# Verify calls
mock_obj.some_method.assert_called_once()
Mock Return Values
from unittest.mock import Mock
# Create mock with return value
mock_func = Mock(return_value=42)
result = mock_func()
print(result) # 42
# Set return value
mock_func.return_value = 100
result = mock_func()
print(result) # 100
Mock Side Effects
from unittest.mock import Mock
# Mock that raises exception
mock_func = Mock(side_effect=ValueError("Error"))
# mock_func() # Raises ValueError
# Mock with multiple side effects
mock_func = Mock(side_effect=[1, 2, 3])
print(mock_func()) # 1
print(mock_func()) # 2
print(mock_func()) # 3
Mock Objects
Creating Mock Objects
from unittest.mock import Mock
# Basic mock
mock = Mock()
# Mock with attributes
mock = Mock(name="test_mock")
mock.attribute = "value"
# Mock with methods
mock = Mock()
mock.method.return_value = "result"
print(mock.method()) # result
Configuring Mocks
from unittest.mock import Mock
# Configure mock
mock = Mock()
mock.method.return_value = 42
mock.attribute = "test"
# Use configured mock
result = mock.method()
print(result) # 42
print(mock.attribute) # test
MagicMock
MagicMock is like Mock but supports magic methods:
from unittest.mock import MagicMock
# MagicMock supports magic methods
mock = MagicMock()
mock.__len__.return_value = 5
print(len(mock)) # 5
# Supports operators
mock = MagicMock()
mock.__str__.return_value = "Mock object"
print(str(mock)) # Mock object
Mock Attributes
from unittest.mock import Mock
# Set attributes
mock = Mock()
mock.attribute = "value"
print(mock.attribute) # value
# Access non-existent attributes (creates new mock)
mock = Mock()
print(mock.new_attribute) # <Mock name='mock.new_attribute' id='...'>
Verifying Mock Calls
Asserting Calls
from unittest.mock import Mock
mock = Mock()
# Call the mock
mock.method(1, 2, key="value")
# Verify it was called
mock.method.assert_called_once()
mock.method.assert_called_with(1, 2, key="value")
mock.method.assert_called_once_with(1, 2, key="value")
Call Count
from unittest.mock import Mock
mock = Mock()
mock.method()
mock.method()
mock.method()
# Check call count
assert mock.method.call_count == 3
mock.method.assert_called()
Call Arguments
from unittest.mock import Mock
mock = Mock()
mock.method(1, 2, key="value")
# Check arguments
args, kwargs = mock.method.call_args
print(args) # (1, 2)
print(kwargs) # {'key': 'value'}
# Check all calls
print(mock.method.call_args_list)
# [call(1, 2, key='value')]
Call History
from unittest.mock import Mock, call
mock = Mock()
mock.method(1)
mock.method(2)
mock.method(3)
# Check call history
expected_calls = [call(1), call(2), call(3)]
mock.method.assert_has_calls(expected_calls)
Patching
@patch Decorator
The @patch decorator replaces objects with mocks:
from unittest.mock import patch
@patch('module.function')
def test_something(mock_function):
mock_function.return_value = 42
result = module.function()
assert result == 42
patch() Context Manager
from unittest.mock import patch
def test_something():
with patch('module.function') as mock_function:
mock_function.return_value = 42
result = module.function()
assert result == 42
Patching Objects
from unittest.mock import patch, Mock
@patch('module.ClassName')
def test_something(mock_class):
mock_instance = Mock()
mock_class.return_value = mock_instance
mock_instance.method.return_value = "result"
obj = module.ClassName()
result = obj.method()
assert result == "result"
Patching Methods
from unittest.mock import patch
class MyClass:
def method(self):
return "real"
@patch.object(MyClass, 'method')
def test_method(mock_method):
mock_method.return_value = "mocked"
obj = MyClass()
result = obj.method()
assert result == "mocked"
Patching Multiple Objects
from unittest.mock import patch
@patch('module.function1')
@patch('module.function2')
def test_multiple(mock_func2, mock_func1):
mock_func1.return_value = 1
mock_func2.return_value = 2
# Test code
Practical Examples
Example 1: Mocking API Calls
from unittest.mock import patch, Mock
import requests
def fetch_data(url):
response = requests.get(url)
return response.json()
@patch('requests.get')
def test_fetch_data(mock_get):
# Mock response
mock_response = Mock()
mock_response.json.return_value = {'data': 'test'}
mock_get.return_value = mock_response
# Test
result = fetch_data('https://api.example.com')
# Assertions
assert result == {'data': 'test'}
mock_get.assert_called_once_with('https://api.example.com')
Example 2: Mocking Database
from unittest.mock import Mock, patch
class Database:
def query(self, sql):
# Real database query
pass
def get_user_count(db):
result = db.query("SELECT COUNT(*) FROM users")
return result
@patch('module.Database')
def test_get_user_count(mock_db_class):
# Mock database instance
mock_db = Mock()
mock_db_class.return_value = mock_db
mock_db.query.return_value = 42
# Test
db = Database()
count = get_user_count(db)
assert count == 42
mock_db.query.assert_called_once_with("SELECT COUNT(*) FROM users")
Example 3: Mocking File Operations
from unittest.mock import patch, mock_open
def read_config(filename):
with open(filename, 'r') as f:
return f.read()
@patch('builtins.open', new_callable=mock_open, read_data='config data')
def test_read_config(mock_file):
result = read_config('config.txt')
assert result == 'config data'
mock_file.assert_called_once_with('config.txt', 'r')
Example 4: Mocking Time
from unittest.mock import patch
import time
def get_timestamp():
return time.time()
@patch('time.time')
def test_get_timestamp(mock_time):
mock_time.return_value = 1234567890
result = get_timestamp()
assert result == 1234567890
Example 5: Mocking Random
from unittest.mock import patch
import random
def roll_dice():
return random.randint(1, 6)
@patch('random.randint')
def test_roll_dice(mock_randint):
mock_randint.return_value = 5
result = roll_dice()
assert result == 5
mock_randint.assert_called_once_with(1, 6)
Advanced Mocking Techniques
Mocking Properties
from unittest.mock import Mock, PropertyMock
class MyClass:
@property
def value(self):
return "real"
@patch.object(MyClass, 'value', new_callable=PropertyMock)
def test_property(mock_value):
mock_value.return_value = "mocked"
obj = MyClass()
assert obj.value == "mocked"
Mocking Context Managers
from unittest.mock import Mock, MagicMock
mock_context = MagicMock()
mock_context.__enter__.return_value = "entered"
mock_context.__exit__.return_value = False
with mock_context as value:
assert value == "entered"
Mocking Iterables
from unittest.mock import Mock
mock_iterable = Mock()
mock_iterable.__iter__.return_value = iter([1, 2, 3])
for item in mock_iterable:
print(item) # 1, 2, 3
Spec and SpecSet
from unittest.mock import Mock
# spec restricts mock to specified attributes
class MyClass:
def method(self):
pass
attribute = "value"
# Mock with spec
mock = Mock(spec=MyClass)
mock.method() # OK
# mock.unknown() # AttributeError
# spec_set is stricter (can't add attributes)
mock = Mock(spec_set=MyClass)
# mock.new_attr = "value" # AttributeError
Common Patterns
Pattern 1: Mock External Service
from unittest.mock import patch
@patch('requests.post')
def test_api_call(mock_post):
mock_post.return_value.json.return_value = {'status': 'success'}
# Test code that uses API
Pattern 2: Mock Database
from unittest.mock import Mock
def test_database_operation():
mock_db = Mock()
mock_db.execute.return_value = 1
# Test code
Pattern 3: Mock File I/O
from unittest.mock import patch, mock_open
@patch('builtins.open', new_callable=mock_open, read_data='content')
def test_file_read(mock_file):
# Test code that reads file
pass
Pattern 4: Mock Time-Dependent Code
from unittest.mock import patch
from datetime import datetime
@patch('datetime.datetime')
def test_time_dependent(mock_datetime):
mock_datetime.now.return_value = datetime(2024, 1, 1)
# Test code
Common Mistakes and Pitfalls
1. Patching Wrong Location
# WRONG: Patching where it's defined
@patch('module.requests.get')
# CORRECT: Patch where it's used
@patch('my_module.requests.get')
2. Not Resetting Mocks
# WRONG: Mock state persists
mock = Mock()
mock.method()
# Mock still has call history
# CORRECT: Reset when needed
mock.reset_mock()
3. Over-Mocking
# WRONG: Mocking everything
@patch('module.function1')
@patch('module.function2')
@patch('module.function3')
# Too many mocks!
# CORRECT: Mock only what's necessary
@patch('module.external_dependency')
4. Not Verifying Calls
# WRONG: Don't verify mock was called
mock.method()
# No assertion
# CORRECT: Verify calls
mock.method.assert_called_once()
Best Practices
1. Mock External Dependencies
# Mock external services, databases, file I/O
@patch('requests.get')
def test_api(mock_get):
pass
2. Use Specs for Type Safety
mock = Mock(spec=MyClass)
3. Verify Important Calls
mock.method.assert_called_once_with(expected_args)
4. Keep Mocks Simple
# Simple mock
mock.return_value = 42
# Avoid complex mock setups
5. Use Context Managers When Appropriate
with patch('module.function') as mock_func:
# Test code
pass
6. Document Mock Behavior
# Mock that simulates API response
mock_api.return_value = {'status': 'success'}
Practice Exercise
Exercise: Mocking
Objective: Create a Python program that demonstrates mocking and patching.
Instructions:
-
Create a file called
test_mocking_practice.py -
Write a program that:
- Creates mock objects
- Uses patching
- Mocks external dependencies
- Verifies mock calls
- Demonstrates practical applications
-
Your program should include:
- Basic Mock usage
- MagicMock usage
- Patching functions
- Patching methods
- Mocking API calls
- Mocking file operations
- Verifying calls
- Real-world examples
Example Solution:
"""
Mocking and Patching Practice
This program demonstrates unittest.mock features.
"""
from unittest.mock import Mock, MagicMock, patch, mock_open, PropertyMock
import unittest
# Code to test
def fetch_user_data(user_id):
import requests
response = requests.get(f'https://api.example.com/users/{user_id}')
return response.json()
def process_file(filename):
with open(filename, 'r') as f:
return f.read().upper()
class UserService:
def __init__(self, db):
self.db = db
def get_user(self, user_id):
return self.db.query(f"SELECT * FROM users WHERE id={user_id}")
# Test cases
class TestMocking(unittest.TestCase):
# 1. Basic Mock
def test_basic_mock(self):
mock = Mock()
mock.method.return_value = 42
result = mock.method()
self.assertEqual(result, 42)
mock.method.assert_called_once()
# 2. Mock with return value
def test_mock_return_value(self):
mock_func = Mock(return_value="result")
self.assertEqual(mock_func(), "result")
# 3. Mock with side effect
def test_mock_side_effect(self):
mock_func = Mock(side_effect=[1, 2, 3])
self.assertEqual(mock_func(), 1)
self.assertEqual(mock_func(), 2)
self.assertEqual(mock_func(), 3)
# 4. Mock that raises exception
def test_mock_exception(self):
mock_func = Mock(side_effect=ValueError("Error"))
with self.assertRaises(ValueError):
mock_func()
# 5. MagicMock
def test_magic_mock(self):
mock = MagicMock()
mock.__len__.return_value = 5
self.assertEqual(len(mock), 5)
# 6. Verifying calls
def test_verify_calls(self):
mock = Mock()
mock.method(1, 2, key="value")
mock.method.assert_called_once()
mock.method.assert_called_with(1, 2, key="value")
# 7. Call count
def test_call_count(self):
mock = Mock()
mock.method()
mock.method()
mock.method()
self.assertEqual(mock.method.call_count, 3)
# 8. Patching function
@patch('requests.get')
def test_patch_function(self, mock_get):
mock_response = Mock()
mock_response.json.return_value = {'id': 1, 'name': 'Alice'}
mock_get.return_value = mock_response
result = fetch_user_data(1)
self.assertEqual(result, {'id': 1, 'name': 'Alice'})
mock_get.assert_called_once_with('https://api.example.com/users/1')
# 9. Patching with context manager
def test_patch_context_manager(self):
with patch('requests.get') as mock_get:
mock_response = Mock()
mock_response.json.return_value = {'status': 'ok'}
mock_get.return_value = mock_response
result = fetch_user_data(1)
self.assertEqual(result, {'status': 'ok'})
# 10. Patching file operations
@patch('builtins.open', new_callable=mock_open, read_data='test content')
def test_patch_file(self, mock_file):
result = process_file('test.txt')
self.assertEqual(result, 'TEST CONTENT')
mock_file.assert_called_once_with('test.txt', 'r')
# 11. Patching method
def test_patch_method(self):
mock_db = Mock()
mock_db.query.return_value = {'id': 1, 'name': 'Alice'}
service = UserService(mock_db)
result = service.get_user(1)
self.assertEqual(result, {'id': 1, 'name': 'Alice'})
mock_db.query.assert_called_once_with("SELECT * FROM users WHERE id=1")
# 12. Patching multiple objects
@patch('module.function2')
@patch('module.function1')
def test_patch_multiple(self, mock_func1, mock_func2):
mock_func1.return_value = 1
mock_func2.return_value = 2
# Test code
pass
# 13. Mock with spec
def test_mock_with_spec(self):
class MyClass:
def method(self):
pass
attribute = "value"
mock = Mock(spec=MyClass)
mock.method() # OK
# mock.unknown() # Would raise AttributeError
# 14. Mock property
def test_mock_property(self):
class MyClass:
@property
def value(self):
return "real"
with patch.object(MyClass, 'value', new_callable=PropertyMock) as mock_prop:
mock_prop.return_value = "mocked"
obj = MyClass()
self.assertEqual(obj.value, "mocked")
# 15. Mock context manager
def test_mock_context_manager(self):
mock_cm = MagicMock()
mock_cm.__enter__.return_value = "entered"
mock_cm.__exit__.return_value = False
with mock_cm as value:
self.assertEqual(value, "entered")
mock_cm.__enter__.assert_called_once()
mock_cm.__exit__.assert_called_once()
# 16. Mock iterable
def test_mock_iterable(self):
mock_iter = Mock()
mock_iter.__iter__.return_value = iter([1, 2, 3])
result = list(mock_iter)
self.assertEqual(result, [1, 2, 3])
# 17. Real-world: Mocking API service
def test_mock_api_service(self):
mock_api = Mock()
mock_api.get_user.return_value = {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}
user = mock_api.get_user(1)
self.assertEqual(user['name'], 'Alice')
mock_api.get_user.assert_called_once_with(1)
# 18. Real-world: Mocking database
def test_mock_database(self):
mock_db = Mock()
mock_db.execute.return_value = 1
mock_db.fetchall.return_value = [{'id': 1, 'name': 'Alice'}]
mock_db.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
users = mock_db.fetchall()
self.assertEqual(len(users), 1)
self.assertEqual(users[0]['name'], 'Alice')
# 19. Real-world: Mocking time
@patch('time.sleep')
def test_mock_time(self, mock_sleep):
import time
time.sleep(5) # Doesn't actually sleep
mock_sleep.assert_called_once_with(5)
# 20. Real-world: Mocking random
@patch('random.randint')
def test_mock_random(self, mock_randint):
import random
mock_randint.return_value = 5
result = random.randint(1, 6)
self.assertEqual(result, 5)
mock_randint.assert_called_once_with(1, 6)
if __name__ == '__main__':
unittest.main()
Expected Output (when running python -m unittest test_mocking_practice.py -v):
test_basic_mock (__main__.TestMocking) ... ok
test_call_count (__main__.TestMocking) ... ok
test_magic_mock (__main__.TestMocking) ... ok
[... rest of output ...]
----------------------------------------------------------------------
Ran 20 tests in 0.002s
OK
Challenge (Optional):
- Create mocks for a complete external API
- Build a test suite that mocks a database layer
- Implement mocks for file system operations
- Create mocks for complex third-party libraries
Key Takeaways
- unittest.mock - Python's mocking module
- Mock objects - fake objects that simulate behavior
- MagicMock - Mock with magic method support
- Patching - replacing objects with mocks
- @patch decorator - decorator for patching
- patch() context manager - context manager for patching
- Verifying calls - assert_called, assert_called_with, etc.
- Mock configuration - return_value, side_effect, spec
- External dependencies - mock APIs, databases, file I/O
- Isolation - test code without dependencies
- Speed - avoid slow operations in tests
- Control - control dependency behavior
- Best practices - mock external deps, use specs, verify calls
- Common mistakes - patching wrong location, over-mocking
- When to use - external dependencies, slow operations, unreliable services
Quiz: Mocking
Test your understanding with these questions:
-
What is mocking?
- A) Testing real objects
- B) Replacing real objects with fake objects
- C) Creating real objects
- D) Deleting objects
-
What module provides mocking in Python?
- A) mock
- B) unittest.mock
- C) testing.mock
- D) fake
-
What is the difference between Mock and MagicMock?
- A) No difference
- B) MagicMock supports magic methods
- C) Mock is faster
- D) MagicMock is slower
-
What does @patch do?
- A) Creates a mock
- B) Replaces object with mock
- C) Deletes object
- D) Nothing
-
Where should you patch an object?
- A) Where it's defined
- B) Where it's used
- C) Anywhere
- D) Nowhere
-
What does return_value do?
- A) Sets return value for mock
- B) Gets return value
- C) Deletes return value
- D) Nothing
-
What does side_effect do?
- A) Sets return value
- B) Sets side effects (exceptions, multiple returns)
- C) Deletes side effects
- D) Nothing
-
How do you verify a mock was called?
- A) mock.assert_called()
- B) mock.was_called()
- C) mock.called()
- D) check(mock)
-
What is spec used for?
- A) Speed up mocks
- B) Restrict mock to specified attributes
- C) Delete attributes
- D) Nothing
-
When should you use mocks?
- A) Always
- B) For external dependencies
- C) Never
- D) Only in unittest
Answers:
- B) Replacing real objects with fake objects (mocking definition)
- B) unittest.mock (Python's mocking module)
- B) MagicMock supports magic methods (difference)
- B) Replaces object with mock (@patch purpose)
- B) Where it's used (patching location)
- A) Sets return value for mock (return_value purpose)
- B) Sets side effects (exceptions, multiple returns) (side_effect purpose)
- A) mock.assert_called() (verifying calls)
- B) Restrict mock to specified attributes (spec purpose)
- B) For external dependencies (when to use mocks)
Next Steps
Excellent work! You've mastered mocking and patching. You now understand:
- unittest.mock module
- Mock objects
- Patching techniques
- When and how to use mocks
What's Next?
- Module 18: Performance Optimization
- Learn about profiling
- Understand performance measurement
- Explore optimization techniques
Additional Resources
- unittest.mock: docs.python.org/3/library/unittest.mock.html
- Mock Objects: en.wikipedia.org/wiki/Mock_object
- Patching: docs.python.org/3/library/unittest.mock.html#patch
Lesson completed! You're ready to move on to the next module.
Course Navigation
- Testing Basics
- pytest Framework
- Test-Driven Development (TDD)
- Mocking and Patching
- Testing Basics
- pytest Framework
- Test-Driven Development (TDD)
- Mocking and Patching