Fixture Sharing
pyrig automatically shares fixtures across all packages in the dependency chain through a multi-package plugin architecture.
How It Works
Every package depending on pyrig inherits all fixtures from pyrig and its dependencies:
Plugin Discovery
The discovery process happens automatically in tests/conftest.py:
Discovery Steps
Detailed Steps:
- Find dependent packages: Uses dependency graph to find all packages depending on pyrig
- Locate fixtures modules: Finds equivalent of
pyrig.dev.tests.fixturesin each package - Collect Python files: Recursively finds all
.pyfiles in fixtures modules - Register as plugins: Adds all files to
pytest_pluginslist
Integration
In Your Package
Pyrig will generate tests/conftest.py:
"""Pytest configuration for tests."""
pytest_plugins = ["pyrig.dev.tests.conftest"]
This single line activates:
- All pyrig fixtures
- All fixtures from packages you depend on
- Automatic fixture discovery for your package
- All autouse fixtures from the entire chain
Adding Custom Fixtures
Create fixtures in your package's fixtures module:
myapp/
└── dev/
└── tests/
└── fixtures/
├── __init__.py
├── my_fixtures.py # Custom fixtures
Important: Unlike the CLI framework (which auto-decorates functions as Typer commands), fixtures must be explicitly decorated with:
@pytest.fixturefrom pytest, or- Scope-specific decorators from
pyrig.dev.utils.testing(@session_fixture,@module_fixture, etc.)
Pyrig does not auto-decorate fixture functions.
These fixtures automatically become available to:
- Your package's tests
- All packages that depend on your package
Built-in Fixtures
Factory Fixtures
config_file_factory
Creates test versions of ConfigFile subclasses using temporary paths:
def test_my_config(config_file_factory):
TestConfig = config_file_factory(MyConfigFile)
# TestConfig.get_path() returns path in tmp_path
config = TestConfig()
assert TestConfig.get_path().exists()
Purpose: Isolate config file tests from actual project files. Prevents file generation in your project if you define custom subclasses of ConfigFile.
Testing BuilderConfigFile subclasses
Use config_file_factory to create test versions of BuilderConfigFile
subclasses using temporary paths:
def test_my_builder(config_file_factory):
TestBuilder = config_file_factory(MyBuilder)
# TestBuilder.get_path() returns path in tmp_path
builder = TestBuilder()
builder.build()
assert TestBuilder.get_parent_path().exists()
Purpose: Isolate artifact generation tests from actual build directories.
Assertion Fixtures
main_test_fixture
Tests that the main entry point works correctly:
def test_main(main_test_fixture: None) -> None:
"""Test main entry point."""
assert main_test_fixture is None
Purpose: Verify CLI --help works and main function is callable.
Fixture Scopes
Use pyrig's scope decorators for custom fixtures:
from pyrig.dev.utils.testing import (
session_fixture,
module_fixture,
class_fixture,
function_fixture,
)
@session_fixture
def database_connection():
"""Shared across entire test session."""
return create_connection()
@module_fixture
def module_setup():
"""Shared across test module."""
return setup_module()
Multi-Package Example
pyrig (base package)
├── fixtures: config_file_factory, main_test_fixture
│
Package A (depends on pyrig)
├── fixtures: database_fixture, api_client_fixture
│ └── Inherits: All pyrig fixtures
│
Package B (depends on Package A)
├── fixtures: custom_fixture
└── Inherits: All pyrig + Package A fixtures
When Package B tests run:
✓ config_file_factory (from pyrig)
✓ main_test_fixture (from pyrig)
✓ database_fixture (from Package A)
✓ api_client_fixture (from Package A)
✓ custom_fixture (from Package B)
All fixtures are automatically discovered and available without any additional configuration.