A pytest plugin that enables the use of fixtures within pytest.mark.parametrize decorators through lazy evaluation
npx @tessl/cli install tessl/pypi-pytest-lazy-fixture@0.6.00
# pytest-lazy-fixture
1
2
A pytest plugin that enables the use of fixtures within `pytest.mark.parametrize` decorators through lazy evaluation. This plugin solves the limitation where fixtures cannot normally be used directly in parametrize decorators by providing a lazy evaluation mechanism that resolves fixture values at test runtime.
3
4
## Package Information
5
6
- **Package Name**: pytest-lazy-fixture
7
- **Language**: Python
8
- **Installation**: `pip install pytest-lazy-fixture`
9
- **Requirements**: pytest>=3.2.5
10
11
## Core Imports
12
13
```python
14
import pytest
15
# The plugin automatically makes lazy_fixture available via pytest namespace
16
pytest.lazy_fixture('fixture_name')
17
```
18
19
Direct import:
20
21
```python
22
from pytest_lazyfixture import lazy_fixture, is_lazy_fixture, LazyFixture
23
```
24
25
## Basic Usage
26
27
```python
28
import pytest
29
30
# Define a fixture with parameters
31
@pytest.fixture(params=[1, 2])
32
def one(request):
33
return request.param
34
35
# Use lazy_fixture in parametrize to reference the fixture
36
@pytest.mark.parametrize('arg1,arg2', [
37
('val1', pytest.lazy_fixture('one')),
38
('val2', pytest.lazy_fixture('one')),
39
])
40
def test_func(arg1, arg2):
41
assert arg2 in [1, 2]
42
43
# Multiple lazy fixtures in one parametrize
44
@pytest.fixture(params=[3, 4])
45
def two(request):
46
return request.param
47
48
@pytest.mark.parametrize('arg1,arg2,arg3', [
49
('val1', pytest.lazy_fixture('one'), pytest.lazy_fixture('two')),
50
])
51
def test_multiple(arg1, arg2, arg3):
52
assert arg2 in [1, 2]
53
assert arg3 in [3, 4]
54
55
# Use in fixture parameters (indirect parametrization)
56
@pytest.fixture(params=[
57
pytest.lazy_fixture('one'),
58
pytest.lazy_fixture('two')
59
])
60
def combined(request):
61
return request.param
62
63
def test_combined(combined):
64
assert combined in [1, 2, 3, 4]
65
```
66
67
## Architecture
68
69
The pytest-lazy-fixture plugin uses pytest's hook system to intercept and modify test collection and execution. The core design enables lazy evaluation through several key components:
70
71
### Hook Integration
72
- **pytest_configure()**: Registers `lazy_fixture` function in pytest namespace for convenient access
73
- **pytest_runtest_setup()**: Modifies test request object to handle lazy fixture resolution during setup
74
- **pytest_fixture_setup()**: Resolves lazy fixtures used as parameters to other fixtures
75
- **pytest_generate_tests()**: Normalizes test generation to expand lazy fixture combinations
76
77
### Lazy Evaluation Mechanism
78
The plugin defers fixture resolution until test runtime rather than collection time:
79
1. **Collection Phase**: LazyFixture objects store fixture names without resolving values
80
2. **Setup Phase**: Plugin hooks intercept test setup and replace LazyFixture objects with actual fixture values
81
3. **Execution Phase**: Tests receive fully resolved fixture values transparently
82
83
### Dependency Resolution
84
- **Dependency Tracking**: Analyzes fixture dependency chains to ensure correct resolution order
85
- **Parameter Expansion**: Handles parametrized fixtures by expanding all combinations of lazy fixture values
86
- **Metafunction Modification**: Modifies pytest's internal test generation to support lazy fixture combinations
87
88
This design allows fixtures to be used in `@pytest.mark.parametrize` decorators while maintaining pytest's normal fixture dependency resolution and scoping rules.
89
90
## Capabilities
91
92
### Lazy Fixture Creation
93
94
Creates lazy fixture references that are resolved at test runtime rather than collection time.
95
96
```python { .api }
97
def lazy_fixture(names):
98
"""
99
Create lazy fixture references for use in parametrize decorators.
100
101
Args:
102
names (str or list): Single fixture name or list of fixture names
103
104
Returns:
105
LazyFixture or list[LazyFixture]: Lazy fixture wrapper(s)
106
"""
107
```
108
109
Usage examples:
110
111
```python
112
# Single fixture
113
lazy_ref = pytest.lazy_fixture('my_fixture')
114
115
# Multiple fixtures
116
lazy_refs = lazy_fixture(['fixture1', 'fixture2'])
117
```
118
119
### Lazy Fixture Detection
120
121
Utility function to check if a value is a lazy fixture instance.
122
123
```python { .api }
124
def is_lazy_fixture(val):
125
"""
126
Check if a value is a LazyFixture instance.
127
128
Args:
129
val: Value to check
130
131
Returns:
132
bool: True if val is a LazyFixture instance
133
"""
134
```
135
136
Usage example:
137
138
```python
139
if is_lazy_fixture(some_value):
140
# Handle lazy fixture
141
actual_value = request.getfixturevalue(some_value.name)
142
```
143
144
### Lazy Fixture Wrapper
145
146
The core wrapper class that holds fixture names for lazy evaluation.
147
148
```python { .api }
149
class LazyFixture(object):
150
"""
151
Wrapper class for lazy fixture evaluation.
152
153
Attributes:
154
name (str): Name of the fixture to be resolved
155
"""
156
157
def __init__(self, name):
158
"""
159
Initialize lazy fixture with fixture name.
160
161
Args:
162
name (str): Name of the fixture to wrap
163
"""
164
165
def __repr__(self):
166
"""
167
String representation of the lazy fixture.
168
169
Returns:
170
str: Formatted string showing class name and fixture name
171
"""
172
173
def __eq__(self, other):
174
"""
175
Compare LazyFixture instances by fixture name.
176
177
Args:
178
other (LazyFixture): Another LazyFixture instance
179
180
Returns:
181
bool: True if fixture names are equal
182
"""
183
```
184
185
### Plugin Integration
186
187
The plugin automatically integrates with pytest through entry points and hooks:
188
189
```python { .api }
190
# Available after plugin installation
191
pytest.lazy_fixture # Function accessible via pytest namespace
192
```
193
194
## Plugin Features
195
196
### Parametrize Integration
197
198
- **Direct parametrization**: Use lazy fixtures as parameter values in `@pytest.mark.parametrize`
199
- **Multiple fixtures**: Support for multiple lazy fixtures in the same parametrize decorator
200
- **Mixed parameters**: Combine lazy fixtures with regular values in parametrize
201
202
### Indirect Parametrization
203
204
- **Fixture parameters**: Use lazy fixtures as parameters to other fixtures
205
- **Fixture chains**: Support for fixtures that depend on other fixtures through lazy evaluation
206
207
### Test ID Generation
208
209
- **Automatic naming**: Test IDs automatically use fixture names for lazy fixtures
210
- **Clear identification**: Easy identification of which fixture values are being tested
211
212
### Dependency Resolution
213
214
- **Automatic resolution**: Lazy fixtures are resolved automatically at test runtime
215
- **Dependency order**: Proper handling of fixture dependency chains
216
- **Parameter sorting**: Internal sorting to ensure fixtures are resolved in correct order
217
218
## Internal Implementation Details
219
220
The plugin works by:
221
222
1. **Hook Integration**: Registers pytest hooks to intercept test collection and execution
223
2. **Lazy Evaluation**: Defers fixture resolution until test runtime using `request.getfixturevalue()`
224
3. **Metafunction Modification**: Modifies pytest's metafunction calls to handle lazy fixtures
225
4. **Dependency Tracking**: Tracks and resolves fixture dependencies in correct order
226
227
Key internal functions (not part of public API):
228
229
- `pytest_runtest_setup()`: Modifies test setup to handle lazy fixtures
230
- `pytest_fixture_setup()`: Resolves lazy fixtures used as fixture parameters
231
- `pytest_generate_tests()`: Normalizes test generation for lazy fixtures
232
- `sorted_by_dependency()`: Ensures fixtures are resolved in dependency order
233
234
## Error Handling
235
236
The plugin handles various pytest version compatibility issues and provides appropriate error handling for:
237
238
- Missing fixtures referenced by lazy_fixture
239
- Circular fixture dependencies
240
- Version compatibility across pytest 3.2.5+ releases
241
242
## Compatibility
243
244
- **Python**: 2.7, 3.4+
245
- **pytest**: 3.2.5+
246
- **Platforms**: Cross-platform (Windows, macOS, Linux)
247
- **Implementation**: CPython, PyPy