0
# Pytest Fixtures
1
2
pytest-mock provides MockerFixture instances through pytest fixtures at multiple scopes, enabling flexible mock management across different test organization patterns. Each fixture provides the same MockerFixture interface but with different lifetimes and cleanup behaviors.
3
4
## Capabilities
5
6
### Function-Scoped Fixture
7
8
The default mocker fixture provides a MockerFixture instance that's created fresh for each test function and automatically cleaned up afterwards.
9
10
```python { .api }
11
@pytest.fixture
12
def mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
13
"""
14
Function-scoped mocker fixture (default scope).
15
16
Creates a new MockerFixture instance for each test function.
17
All mocks are automatically stopped after the test completes.
18
19
Yields:
20
MockerFixture instance for the test function
21
"""
22
```
23
24
Usage examples:
25
26
```python
27
def test_function_scope_basic(mocker):
28
# Fresh mocker instance for this test
29
mock_func = mocker.patch('os.path.exists')
30
mock_func.return_value = True
31
32
result = check_file_exists('test.txt')
33
assert result is True
34
mock_func.assert_called_once_with('test.txt')
35
# Mock automatically cleaned up after test
36
37
def test_function_scope_independent(mocker):
38
# Completely independent mocker instance
39
mock_func = mocker.patch('os.path.exists')
40
mock_func.return_value = False
41
42
result = check_file_exists('other.txt')
43
assert result is False
44
# No interference from previous test
45
46
class TestWithFunctionScope:
47
def test_method_one(self, mocker):
48
# Fresh mocker for this method
49
spy = mocker.spy(Calculator, 'add')
50
calc = Calculator()
51
result = calc.add(2, 3)
52
spy.assert_called_once_with(2, 3)
53
54
def test_method_two(self, mocker):
55
# Independent mocker instance
56
mock_calc = mocker.create_autospec(Calculator)
57
mock_calc.add.return_value = 100
58
assert mock_calc.add(1, 1) == 100
59
```
60
61
### Class-Scoped Fixture
62
63
The class_mocker fixture provides a MockerFixture instance shared across all test methods in a test class, with cleanup after the class completes.
64
65
```python { .api }
66
@pytest.fixture(scope="class")
67
def class_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
68
"""
69
Class-scoped mocker fixture.
70
71
Creates one MockerFixture instance shared by all test methods
72
in a test class. Mocks persist across methods and are cleaned
73
up after the class completes.
74
75
Yields:
76
MockerFixture instance for the test class
77
"""
78
```
79
80
Usage examples:
81
82
```python
83
class TestDatabaseOperations:
84
def test_connection_setup(self, class_mocker):
85
# Mock persists for entire class
86
self.db_mock = class_mocker.patch('myapp.database.connect')
87
self.db_mock.return_value = 'mock_connection'
88
89
setup_database()
90
self.db_mock.assert_called_once()
91
92
def test_query_execution(self, class_mocker):
93
# Same mocker instance, mocks still active
94
query_mock = class_mocker.patch('myapp.database.execute_query')
95
query_mock.return_value = [{'id': 1, 'name': 'test'}]
96
97
results = run_query('SELECT * FROM users')
98
99
# db_mock from previous test is still active
100
assert self.db_mock.called
101
query_mock.assert_called_once()
102
assert len(results) == 1
103
104
def test_connection_cleanup(self, class_mocker):
105
# All mocks from class still available
106
cleanup_mock = class_mocker.patch('myapp.database.disconnect')
107
108
cleanup_database()
109
110
# Can verify interactions across the whole class
111
assert self.db_mock.call_count >= 1
112
cleanup_mock.assert_called_once()
113
114
# All mocks automatically cleaned up after class completes
115
116
# Different class gets fresh mocker instance
117
class TestFileOperations:
118
def test_independent_mocking(self, class_mocker):
119
# Completely separate from TestDatabaseOperations
120
file_mock = class_mocker.patch('builtins.open')
121
file_mock.return_value.__enter__.return_value.read.return_value = 'content'
122
123
data = read_file('test.txt')
124
assert data == 'content'
125
```
126
127
### Module-Scoped Fixture
128
129
The module_mocker fixture provides a MockerFixture instance shared across all tests in a module, with cleanup after the module completes.
130
131
```python { .api }
132
@pytest.fixture(scope="module")
133
def module_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
134
"""
135
Module-scoped mocker fixture.
136
137
Creates one MockerFixture instance shared by all tests
138
in a module. Mocks persist across functions and classes
139
and are cleaned up after the module completes.
140
141
Yields:
142
MockerFixture instance for the test module
143
"""
144
```
145
146
Usage examples:
147
148
```python
149
# test_api_integration.py
150
151
# Module-level setup with module_mocker
152
@pytest.fixture(scope="module", autouse=True)
153
def setup_api_mocks(module_mocker):
154
"""Setup common mocks for entire module."""
155
# Mock external API calls for all tests in module
156
module_mocker.patch('requests.get', side_effect=mock_api_response)
157
module_mocker.patch('requests.post', side_effect=mock_api_post)
158
159
# Mock authentication for all tests
160
auth_mock = module_mocker.patch('myapp.auth.verify_token')
161
auth_mock.return_value = True
162
163
return module_mocker
164
165
def test_get_user_data(module_mocker):
166
# Uses module-level mocks automatically
167
user_data = fetch_user_data(user_id=123)
168
169
# Can add test-specific mocks too
170
cache_mock = module_mocker.patch('myapp.cache.get')
171
cached_data = get_cached_user(user_id=123)
172
173
assert user_data['id'] == 123
174
cache_mock.assert_called_once()
175
176
def test_create_user(module_mocker):
177
# Same module-level mocks still active
178
new_user = create_user({'name': 'Test User'})
179
180
# requests.post mock from setup is used
181
assert new_user['status'] == 'created'
182
183
class TestUserManagement:
184
def test_update_user(self, module_mocker):
185
# Module-level mocks available in classes too
186
result = update_user(123, {'name': 'Updated'})
187
assert result['success'] is True
188
189
def test_delete_user(self, module_mocker):
190
# All module mocks persist across class methods
191
result = delete_user(123)
192
assert result['deleted'] is True
193
194
# All module-level mocks cleaned up when module completes
195
```
196
197
### Package-Scoped Fixture
198
199
The package_mocker fixture provides a MockerFixture instance shared across all tests in a Python package, with cleanup after the package completes.
200
201
```python { .api }
202
@pytest.fixture(scope="package")
203
def package_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
204
"""
205
Package-scoped mocker fixture.
206
207
Creates one MockerFixture instance shared by all tests
208
in a package. Mocks persist across modules and are
209
cleaned up after the package completes.
210
211
Yields:
212
MockerFixture instance for the test package
213
"""
214
```
215
216
Usage examples:
217
218
```python
219
# tests/conftest.py - Package-level configuration
220
@pytest.fixture(scope="package", autouse=True)
221
def package_setup(package_mocker):
222
"""Setup package-wide mocks and configuration."""
223
# Mock external services for entire test package
224
package_mocker.patch('myapp.external.payment_service.charge')
225
package_mocker.patch('myapp.external.email_service.send')
226
227
# Mock environment configuration
228
package_mocker.patch.dict('os.environ', {
229
'DATABASE_URL': 'sqlite:///:memory:',
230
'REDIS_URL': 'redis://localhost:6379/15',
231
'API_KEY': 'test-api-key'
232
})
233
234
return package_mocker
235
236
# tests/test_payments.py
237
def test_process_payment(package_mocker):
238
# Uses package-level payment service mock
239
result = process_payment(amount=100, card_token='tok_123')
240
assert result['status'] == 'success'
241
242
# tests/test_notifications.py
243
def test_send_notification(package_mocker):
244
# Uses package-level email service mock
245
send_welcome_email('user@example.com')
246
# Email mock is already configured at package level
247
248
# tests/integration/test_workflows.py
249
def test_complete_workflow(package_mocker):
250
# All package-level mocks available in subdirectories
251
workflow = CompleteUserWorkflow()
252
result = workflow.execute(user_data={'email': 'test@example.com'})
253
254
# Payment and email mocks from package level are used
255
assert result['payment_processed'] is True
256
assert result['welcome_sent'] is True
257
```
258
259
### Session-Scoped Fixture
260
261
The session_mocker fixture provides a MockerFixture instance shared across the entire test session, with cleanup after all tests complete.
262
263
```python { .api }
264
@pytest.fixture(scope="session")
265
def session_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
266
"""
267
Session-scoped mocker fixture.
268
269
Creates one MockerFixture instance shared by all tests
270
in the session. Mocks persist across all packages and
271
modules and are cleaned up after the session completes.
272
273
Yields:
274
MockerFixture instance for the test session
275
"""
276
```
277
278
Usage examples:
279
280
```python
281
# conftest.py at root of test suite
282
@pytest.fixture(scope="session", autouse=True)
283
def global_test_setup(session_mocker):
284
"""Setup session-wide mocks for external dependencies."""
285
# Mock third-party services for entire test session
286
session_mocker.patch('stripe.api_resources.Charge.create')
287
session_mocker.patch('boto3.client') # Mock AWS services
288
session_mocker.patch('redis.Redis.from_url')
289
290
# Mock time-based functions for consistent testing
291
import datetime
292
fixed_time = datetime.datetime(2023, 1, 1, 12, 0, 0)
293
session_mocker.patch('datetime.datetime.now', return_value=fixed_time)
294
295
return session_mocker
296
297
# Any test file in the session
298
def test_stripe_integration(session_mocker):
299
# Uses session-level stripe mock automatically
300
charge = create_payment_charge(amount=1000)
301
assert charge['status'] == 'succeeded'
302
303
def test_aws_s3_upload(session_mocker):
304
# Uses session-level boto3 mock automatically
305
result = upload_file_to_s3('test.txt', 'content')
306
assert result['success'] is True
307
308
# Different package, same session mocks
309
def test_redis_caching(session_mocker):
310
# Uses session-level redis mock automatically
311
cache_key = 'test:key'
312
set_cache(cache_key, 'value')
313
value = get_cache(cache_key)
314
assert value == 'value'
315
316
def test_time_dependent_logic(session_mocker):
317
# Uses session-level datetime mock - consistent time across all tests
318
timestamp = get_current_timestamp()
319
assert timestamp == '2023-01-01T12:00:00'
320
```
321
322
### Fixture Scope Comparison
323
324
```python
325
# Scope lifecycles and use cases
326
327
def test_scope_demonstration():
328
"""
329
Fixture Scopes (from shortest to longest lived):
330
331
1. function (default) - New MockerFixture per test function
332
- Use for: Isolated test mocks, no shared state needed
333
- Cleanup: After each test function
334
335
2. class - One MockerFixture per test class
336
- Use for: Shared setup within related test methods
337
- Cleanup: After test class completes
338
339
3. module - One MockerFixture per test module
340
- Use for: Expensive mocks, module-wide configuration
341
- Cleanup: After test module completes
342
343
4. package - One MockerFixture per test package
344
- Use for: Package-wide external service mocks
345
- Cleanup: After test package completes
346
347
5. session - One MockerFixture for entire test session
348
- Use for: Global mocks, third-party service mocks
349
- Cleanup: After all tests complete
350
"""
351
352
# Choosing the right scope:
353
def test_choosing_fixture_scope():
354
"""
355
Guidelines for fixture scope selection:
356
357
- function: Default choice, ensures test isolation
358
- class: When test methods need shared mock state
359
- module: For expensive-to-create mocks used across module
360
- package: For mocking external dependencies across packages
361
- session: For global mocks that should never change
362
363
Trade-offs:
364
- Longer scopes = better performance, less isolation
365
- Shorter scopes = better isolation, more setup overhead
366
"""
367
```