0
# Test Collection and Execution
1
2
Collection tree components for test discovery, organization, and execution. The collection system builds a hierarchical tree of collectors and items that represent the structure of your test suite.
3
4
## Capabilities
5
6
### Base Collection Classes
7
8
Foundation classes for all collection tree components.
9
10
```python { .api }
11
class Node:
12
"""Base class for all collection tree components."""
13
14
# Core attributes
15
name: str # Node name
16
parent: Node | None # Parent node
17
config: Config # pytest configuration
18
session: Session # Test session
19
path: Path # File system path
20
nodeid: str # Unique node identifier
21
22
@classmethod
23
def from_parent(cls, parent: Node, **kwargs):
24
"""Create node with parent reference."""
25
26
def listchain(self) -> list[Node]:
27
"""Return list of all parent nodes."""
28
29
def add_marker(self, marker) -> None:
30
"""Add marker to this node."""
31
32
def iter_markers(self, name: str | None = None):
33
"""Iterate over markers."""
34
35
def get_closest_marker(self, name: str):
36
"""Get closest marker with given name."""
37
38
class Collector(Node, abc.ABC):
39
"""Base class for collection tree internal nodes.
40
41
Collectors create children through collect() and iteratively build
42
the collection tree.
43
"""
44
45
class CollectError(Exception):
46
"""An error during collection, contains a custom message."""
47
48
@abc.abstractmethod
49
def collect(self) -> Iterable[Item | Collector]:
50
"""
51
Collect children (items and collectors) for this collector.
52
53
Returns:
54
Iterable of Item or Collector objects
55
"""
56
57
def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr:
58
"""
59
Return a representation of a collection failure.
60
61
Parameters:
62
- excinfo: Exception information for the failure
63
64
Returns:
65
String or TerminalRepr representation of the failure
66
"""
67
68
class Item(Node, abc.ABC):
69
"""Base class for collection tree leaf nodes (test items).
70
71
Note that for a single function there might be multiple test invocation items.
72
"""
73
74
nextitem = None # Reference to next item
75
76
def __init__(
77
self,
78
name,
79
parent=None,
80
config: Config | None = None,
81
session: Session | None = None,
82
nodeid: str | None = None,
83
**kw,
84
) -> None:
85
"""
86
Initialize test item.
87
88
Parameters:
89
- name: Item name
90
- parent: Parent collector
91
- config: pytest configuration
92
- session: Test session
93
- nodeid: Node identifier
94
"""
95
96
@abc.abstractmethod
97
def runtest(self) -> None:
98
"""
99
Run the test case (abstract method).
100
101
Must be implemented by subclasses to execute the actual test.
102
"""
103
104
def setup(self) -> None:
105
"""Set up test execution."""
106
107
def teardown(self) -> None:
108
"""Tear down after test execution."""
109
110
def add_report_section(self, when: str, key: str, content: str) -> None:
111
"""
112
Add report section to test results.
113
114
Parameters:
115
- when: Test phase ("setup", "call", "teardown")
116
- key: Section identifier
117
- content: Section content
118
"""
119
120
def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]:
121
"""
122
Return location information for reporting.
123
124
Returns:
125
Tuple of (filename, line_number, test_name)
126
"""
127
128
@property
129
def location(self) -> tuple[str, int | None, str]:
130
"""Get test location as (relfspath, lineno, testname)."""
131
132
# Attributes
133
user_properties: list[tuple[str, object]] # User-defined properties
134
_report_sections: list[tuple[str, str, str]] # Report sections
135
```
136
137
### File System Collectors
138
139
Collectors for file system entities.
140
141
```python { .api }
142
class FSCollector(Collector, abc.ABC):
143
"""Base class for filesystem-based collectors."""
144
145
def __init__(
146
self,
147
path: Path,
148
parent: Collector | None = None,
149
config: Config | None = None,
150
session: Session | None = None,
151
nodeid: str | None = None,
152
) -> None:
153
"""Initialize filesystem collector with path."""
154
155
class File(FSCollector, abc.ABC):
156
"""Base class for collecting tests from files."""
157
158
@abc.abstractmethod
159
def collect(self) -> Iterable[Item | Collector]:
160
"""Collect tests from file (abstract method)."""
161
162
class Directory(FSCollector, abc.ABC):
163
"""Base class for collecting from directories."""
164
165
@abc.abstractmethod
166
def collect(self) -> Iterable[Item | Collector]:
167
"""Collect from directory contents (abstract method)."""
168
169
class Dir(Directory):
170
"""Directory collector implementation.
171
172
Used for directories without __init__.py files.
173
"""
174
175
def collect(self) -> Iterable[nodes.Item | nodes.Collector]:
176
"""
177
Collect files and subdirectories.
178
179
Returns:
180
Iterable of collectors for Python files and subdirectories
181
"""
182
183
@classmethod
184
def from_parent(cls, parent: nodes.Collector, *, path: Path) -> Self:
185
"""
186
Create Dir collector from parent.
187
188
Parameters:
189
- parent: Parent collector
190
- path: Directory path
191
192
Returns:
193
Dir collector instance
194
"""
195
```
196
197
### Python-Specific Collectors
198
199
Collectors specialized for Python test code.
200
201
```python { .api }
202
class PyCollector(Collector, abc.ABC):
203
"""Base class for Python-specific collectors."""
204
205
def _getobj(self):
206
"""Import and return the Python object."""
207
208
class Module(nodes.File, PyCollector):
209
"""Collector for test classes and functions in Python modules."""
210
211
def collect(self) -> Iterable[nodes.Item | nodes.Collector]:
212
"""
213
Collect test classes and functions from module.
214
215
Returns:
216
Iterable of Class collectors and Function items
217
"""
218
219
def _getobj(self):
220
"""
221
Import and return the module object.
222
223
Returns:
224
The imported module
225
"""
226
227
def _register_setup_module_fixture(self) -> None:
228
"""Register setup/teardown module fixtures."""
229
230
def _register_setup_function_fixture(self) -> None:
231
"""Register setup/teardown function fixtures."""
232
233
class Package(nodes.Directory):
234
"""Collector for Python packages (directories with __init__.py)."""
235
236
def collect(self) -> Iterable[nodes.Item | nodes.Collector]:
237
"""
238
Collect modules and subpackages.
239
240
Returns:
241
Iterable of Module and Package collectors
242
"""
243
244
def setup(self) -> None:
245
"""Set up package-level fixtures."""
246
247
class Class(PyCollector):
248
"""Collector for test methods (and nested classes) in Python classes."""
249
250
def collect(self) -> Iterable[nodes.Item | nodes.Collector]:
251
"""
252
Collect test methods from class.
253
254
Returns:
255
Iterable of Function items and nested Class collectors
256
"""
257
258
def newinstance(self):
259
"""
260
Create new instance of the class.
261
262
Returns:
263
New instance of the test class
264
"""
265
266
@classmethod
267
def from_parent(cls, parent, *, name, obj=None, **kw) -> Self:
268
"""
269
Create Class collector from parent.
270
271
Parameters:
272
- parent: Parent collector
273
- name: Class name
274
- obj: Class object (optional)
275
276
Returns:
277
Class collector instance
278
"""
279
280
def _register_setup_class_fixture(self) -> None:
281
"""Register class-level setup/teardown fixtures."""
282
283
def _register_setup_method_fixture(self) -> None:
284
"""Register method-level setup/teardown fixtures."""
285
286
class Function(Item):
287
"""Item for Python test functions."""
288
289
# Core attributes
290
function: Callable # Test function object
291
fixturenames: frozenset[str] # Required fixture names
292
callspec: CallSpec2 | None # Parametrization info
293
294
def runtest(self) -> None:
295
"""Execute the test function."""
296
297
def setup(self) -> None:
298
"""Set up fixtures and test environment."""
299
300
def teardown(self) -> None:
301
"""Tear down fixtures and clean up."""
302
```
303
304
### Session Management
305
306
Root-level session management for test execution coordination.
307
308
```python { .api }
309
class Session(Collector):
310
"""Root of collection tree, collects initial paths."""
311
312
def collect(self) -> list[Node]:
313
"""Collect from initial file paths."""
314
315
def pytest_runtest_protocol(self, item: Item) -> bool:
316
"""Run test protocol for item."""
317
318
def pytest_collection_modifyitems(self, items: list[Item]) -> None:
319
"""Modify collected items."""
320
```
321
322
### Test Parametrization
323
324
Metafunc object for parametrizing tests during collection.
325
326
```python { .api }
327
class Metafunc:
328
"""Used in pytest_generate_tests hook for parametrizing tests."""
329
330
# Core attributes
331
function: Callable # Test function
332
module: Any # Test module
333
cls: type | None # Test class (if applicable)
334
config: Config # pytest configuration
335
definition: FunctionDefinition # Function definition
336
fixturenames: frozenset[str] # Fixture names used by function
337
338
def parametrize(
339
self,
340
argnames: str | list[str] | tuple[str, ...],
341
argvalues,
342
*,
343
indirect: bool | list[str] = False,
344
ids=None,
345
scope: str | None = None
346
) -> None:
347
"""Parametrize test function."""
348
349
def addcall(self, **kwargs) -> None:
350
"""Add individual test call (deprecated, use parametrize)."""
351
```
352
353
**Usage Example:**
354
355
```python
356
# conftest.py
357
def pytest_generate_tests(metafunc):
358
if "browser" in metafunc.fixturenames:
359
metafunc.parametrize(
360
"browser",
361
["chrome", "firefox", "safari"],
362
ids=["Chrome", "Firefox", "Safari"]
363
)
364
365
if "database" in metafunc.fixturenames:
366
metafunc.parametrize(
367
"database",
368
[("sqlite", ":memory:"), ("postgresql", "test_db")],
369
ids=["SQLite", "PostgreSQL"]
370
)
371
372
# Test will run 6 times (3 browsers × 2 databases)
373
def test_web_app(browser, database):
374
pass
375
```
376
377
### Doctest Integration
378
379
Support for running doctests as part of the test suite.
380
381
```python { .api }
382
class DoctestItem(Item):
383
"""Test item for executing doctests."""
384
385
def __init__(
386
self,
387
name: str,
388
parent: DoctestTextfile | DoctestModule,
389
runner: doctest.DocTestRunner,
390
dtest: doctest.DocTest,
391
) -> None:
392
"""
393
Initialize doctest item.
394
395
Parameters:
396
- name: Item name
397
- parent: Parent collector (DoctestTextfile or DoctestModule)
398
- runner: Doctest runner
399
- dtest: Doctest object
400
"""
401
402
# Attributes
403
runner: doctest.DocTestRunner # Doctest runner
404
dtest: doctest.DocTest # Doctest object
405
obj = None # No underlying Python object
406
_fixtureinfo: fixtures.FuncFixtureInfo # Fixture information
407
fixturenames: frozenset[str] # Required fixture names
408
409
def runtest(self) -> None:
410
"""
411
Execute the doctest.
412
413
Runs the doctest using the configured runner and handles
414
any failures according to pytest conventions.
415
"""
416
417
def setup(self) -> None:
418
"""Set up doctest fixtures and environment."""
419
420
def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr:
421
"""
422
Format doctest failures for display.
423
424
Parameters:
425
- excinfo: Exception information
426
427
Returns:
428
Formatted failure representation
429
"""
430
431
def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]:
432
"""
433
Get doctest location information for reporting.
434
435
Returns:
436
Tuple of (filename, line_number, doctest_name)
437
"""
438
439
@classmethod
440
def from_parent(
441
cls,
442
parent: DoctestTextfile | DoctestModule,
443
*,
444
name: str,
445
runner: doctest.DocTestRunner,
446
dtest: doctest.DocTest,
447
) -> Self:
448
"""
449
Create DoctestItem from parent collector.
450
451
Parameters:
452
- parent: Parent collector
453
- name: Item name
454
- runner: Doctest runner
455
- dtest: Doctest object
456
457
Returns:
458
DoctestItem instance
459
"""
460
```
461
462
## Collection Process
463
464
The collection process follows these steps:
465
466
1. **Start Collection**: Session.collect() begins from initial paths
467
2. **Directory Collection**: Dir collectors process directories
468
3. **File Collection**: Module collectors process Python files
469
4. **Class Collection**: Class collectors process test classes
470
5. **Function Collection**: Function collectors process test functions
471
6. **Parametrization**: Metafunc.parametrize() creates multiple test variants
472
7. **Item Modification**: pytest_collection_modifyitems hook allows final changes
473
474
**Usage Example:**
475
476
```python
477
# Custom collector example
478
class YamlFile(pytest.File):
479
def collect(self):
480
# Parse YAML file and create test items
481
raw = yaml.safe_load(self.path.open())
482
for name, spec in raw.items():
483
yield YamlTest.from_parent(self, name=name, spec=spec)
484
485
class YamlTest(pytest.Item):
486
def __init__(self, name, parent, spec):
487
super().__init__(name, parent)
488
self.spec = spec
489
490
def runtest(self):
491
# Execute test based on YAML spec
492
pass
493
494
def pytest_collect_file(parent, path):
495
if path.suffix == ".yaml" and path.name.startswith("test_"):
496
return YamlFile.from_parent(parent, path=path)
497
```
498
499
## Types
500
501
```python { .api }
502
from typing import Any, Callable
503
from pathlib import Path
504
505
class CallSpec2:
506
"""Represents parametrized test call specification."""
507
508
# Attributes
509
params: dict[str, Any] # Parameter values
510
funcargs: dict[str, Any] # Function arguments
511
id: str | None # Parameter set ID
512
marks: list[Mark] # Applied marks
513
514
def setmulti(self, **kwargs) -> None:
515
"""Set multiple parameters."""
516
517
class FunctionDefinition:
518
"""Definition of a test function for collection."""
519
520
# Attributes
521
name: str # Function name
522
obj: Callable # Function object
523
parent: Node # Parent collector
524
525
def getparent(self, cls: type) -> Node | None:
526
"""Get parent of specific type."""
527
```