Testing
Abacus uses pytest for automated tests, plus local verification scripts for the broader confidence checks that glue linting, smoke paths, and packaging together. The expected workflow is to run targeted tests while you iterate and then run the wider local verification commands before you finish substantial work.
Test Layout
| Path | What it covers |
|---|---|
tests/test_*.py |
Shared infrastructure such as model IO, paths, package identity, and root-level helpers |
tests/mmm/ |
MMM behaviour at the public surface |
tests/mmm/models/ |
Extracted panel implementation seams |
tests/mmm/components/ |
Adstock and saturation component behaviour |
tests/mmm/plotting/ |
Static plotting helpers and theme/layout behaviour |
tests/mmm/optimization/ |
Budget optimisation logic and wrappers |
tests/mmm/diagnostics/ |
Structured diagnostics compute |
tests/mmm/summarization/ |
Summary/export helpers |
When you change a specific module seam, add or update tests in the matching test area instead of only asserting through a broad end-to-end test.
Core Commands
Fast targeted runs
Use targeted runs first. They are faster to interpret and make regressions easier to localise.
Whole-suite pytest
This installs the test extras and runs pytest with the local runtime defaults
from the Makefile.
Local verification
make verify_local is the preferred baseline for substantial changes because
it runs:
- Ruff on retained source, tests, and local runner scripts
- byte-compilation checks
- targeted pytest batches across the shared and MMM surfaces
- the local MMM smoke pipeline
make verify_local_all adds the package build/install smoke path.
Packaging smoke
Run this when any of the following changed:
- packaging metadata in
pyproject.toml - import surfaces or compatibility facades
- bundled assets under
abacus/ - install-time behaviour or README/package artefacts
Runtime Environment
Some test paths need writable cache directories. The recommended defaults are:
The local verification scripts apply these defaults automatically.
Special Cases
Plotting tests
Prefer tests that inspect stable properties such as axes, labels, colours, sizes, rcParams, and return types. Avoid brittle pixel-perfect assertions.
When needed, you can run the top-level plot suite the same way the local verification script does:
Save/load and compatibility work
If you change model serialisation, identity strings, or import compatibility, add tests that prove older saved data or old import paths still work where that compatibility is expected.
Packaging and bundled assets
If you add or move package data, use make verify_package so the change is
checked against an installed wheel rather than only the editable repo checkout.
What to Run Before You Finish
Small, localised change
- Targeted
pytest - Targeted
ruff check
Moderate code change
- Targeted
pytest make check_lintmake smoke_mmm
Broad or risky change
make verify_localmake verify_packageif packaging or bundled assets changed
Writing Good Tests
- Test observable behaviour, not implementation noise.
- Keep fixtures close to the layer you are testing.
- Prefer additive compatibility tests when preserving old behaviour.
- Use small synthetic data where possible.
- For plotting and serialisation, assert the stable contract rather than fragile internals.