special.rst
Overview
This file documents a pytest session-scoped fixture example that demonstrates how to access and manipulate all collected test items before any tests run. The key functionality is embodied in a fixture named `callattr_ahead_of_alltests`, which scans all collected test classes in the test session and invokes a class-level method named `callme` if defined. This enables setup or initialization logic to be executed once per test class ahead of the test execution, regardless of the test framework used (native pytest or unittest).
Such a fixture is useful for performing preparatory actions or side effects related to test classes before any of their test methods run, enhancing test orchestration beyond just individual test setups.
Detailed Explanation
Fixture: callattr_ahead_of_alltests
@pytest.fixture(scope="session", autouse=True)
def callattr_ahead_of_alltests(request):
print("callattr_ahead_of_alltests called")
seen = {None}
session = request.node
for item in session.items:
cls = item.getparent(pytest.Class)
if cls not in seen:
if hasattr(cls.obj, "callme"):
cls.obj.callme()
seen.add(cls)
Purpose:
Runs once per test session, automatically and ahead of all tests. It iterates over all collected test items, identifies their parent test classes, checks for a class methodcallme, and calls it if present.Parameters:
request: pytest's built-in fixture that provides information about the executing test context.
Returns:
None. This fixture performs side effects by calling class methods and printing output.Detailed behavior:
Uses
request.nodeto access the test session node.Iterates over
session.items, which are all collected test items (functions, methods).Uses
item.getparent(pytest.Class)to find the test class container for each test item.Uses a
seenset to ensure each test class'scallmeis called only once.Calls
cls.obj.callme()if the method exists on the class object.Prints a message when the fixture itself is invoked.
Usage Example:
Place this fixture in a
conftest.pyfile in your test root or relevant directory:import pytest @pytest.fixture(scope="session", autouse=True) def callattr_ahead_of_alltests(request): print("callattr_ahead_of_alltests called") seen = {None} session = request.node for item in session.items: cls = item.getparent(pytest.Class) if cls not in seen: if hasattr(cls.obj, "callme"): cls.obj.callme() seen.add(cls)
Test Classes Defining callme
Example test classes illustrating the use of this fixture:
class TestHello:
@classmethod
def callme(cls):
print("callme called!")
def test_method1(self):
print("test_method1 called")
def test_method2(self):
print("test_method2 called")
class TestOther:
@classmethod
def callme(cls):
print("callme other called")
def test_other(self):
print("test other")
import unittest
class SomeTest(unittest.TestCase):
@classmethod
def callme(cls):
print("SomeTest callme called")
def test_unit1(self):
print("test_unit1 method called")
These classes define a class method named
callmewhich will be invoked once per class before any test methods run.Compatible with both pytest-style test classes and unittest-based test classes.
Demonstrates flexibility and integration with different test styles.
Important Implementation Details
The fixture leverages pytest's internal test collection architecture:
request.nodereferences the test session, andsession.itemscontains all collected test items.The method
getparent(pytest.Class)traverses the node hierarchy to find the test class node for each test item.The
seenset prevents duplicate calls in case multiple test items belong to the same test class.The fixture is marked
scope="session"andautouse=True, so it runs automatically once before any tests start.This approach allows running arbitrary code at the class level before executing any tests, which is not directly possible with standard pytest fixtures scoped at function or class level.
Interactions with Other System Parts
This fixture is intended for inclusion in
conftest.py, which is a special pytest configuration file that augments test discovery and setup.It interacts directly with pytest’s test collection mechanism.
Test classes in any test module can define a
callmeclass method to hook into this fixture.It works seamlessly with standard pytest tests as well as unittest-based tests, enabling a unified pre-test-class execution hook.
Does not require changes to individual test files beyond optionally defining
callme.The fixture’s print statements provide runtime feedback, which can be seen by disabling output capture (
-sflag in pytest).
Example pytest output when running test_module.py without output capture:
$ pytest -q -s test_module.py
callattr_ahead_of_alltests called
callme called!
callme other called
SomeTest callme called
test_method1 called
.test_method2 called
.test other
.test_unit1 method called
.
4 passed in 0.12s
Mermaid Diagram: Structure of callattr_ahead_of_alltests Fixture
flowchart TD
A[Start: Test Session Begins]
B[callattr_ahead_of_alltests Fixture Invoked]
C[Get session node from request]
D[Iterate over session.items (all test items)]
E[For each item, get parent test class]
F{Has this class been processed?}
G{Does class define 'callme'?}
H[Call cls.obj.callme()]
I[Add class to seen set]
J[Continue iteration]
K[Fixture ends, tests proceed]
A --> B --> C --> D --> E --> F
F -- No --> G
F -- Yes --> J
G -- Yes --> H --> I --> J
G -- No --> I --> J
J --> D
D -- End of items --> K
Summary
This file provides a practical example and explanation of how to implement a pytest session-scoped fixture that accesses all collected tests and triggers class-level hooks before any test executes. It highlights pytest’s extensibility and the ability to integrate setup behavior at a granular level, supporting both pytest-native and unittest-style test classes. The documented approach is valuable for test orchestration, instrumentation, or global pre-test initialization in complex testing scenarios.