Mirror Tests
pyrig enforces a 1:1 structural mapping between source code and tests. Every
source module, class, function, and method must have a corresponding test
counterpart. This is not a coverage metric — it is a structural guarantee
enforced by the pyrig sync pre-commit hook.
The Convention
The test tree mirrors the source tree exactly:
| Source | Expected test counterpart |
|---|---|
src/my_project/utils.py |
tests/test_my_project/test_utils.py |
def do_something() |
def test_do_something() |
class MyHelper |
class TestMyHelper |
def MyHelper.process() |
def TestMyHelper.test_process() |
How It Works
MirrorTestConfigFile is a ConfigFile subclass. Instead of managing a config
file like pyproject.toml, it manages test files. Its _configs() returns the
complete expected test module content: existing tests merged with new stubs for
every source symbol that has no test yet.
validate() (inherited from ConfigFile) writes the result when the file is
missing or stubs are absent — it never removes or modifies existing test
implementations.
At sync time, MirrorTestConfigFile dynamically generates one subclass per
source module via concrete_subclasses(), so the set of managed test files
always matches the current source package — no registration or manifest needed.
Generated Stubs
Each missing test gets a minimal stub:
def test_do_something() -> None:
"""Test function."""
raise NotImplementedError
class TestMyHelper:
"""Test class."""
def test_process(self) -> None:
"""Test method."""
raise NotImplementedError
Stubs raise NotImplementedError so unimplemented tests fail explicitly rather
than passing silently.
Enforcement
pyrig sync is run as a pre-commit hook. If any test stubs are missing it
creates them, the hook fails, and the developer stages the new files before
recommitting. This means a commit can never introduce untested code.
Customising
MirrorTestConfigFile is a DependencySubclass. Run pyrig mk subcls and
select it to generate a skeleton. Override methods to change naming conventions,
adjust what counts as tested, or customise the stub format. All sync operations
use MirrorTestConfigFile.L (the leaf subclass), so overrides are picked up
automatically.