Unit Tests#
Unit tests cover library code in src/simtools/ at module and function level.
Application workflows are covered by integration tests. Application modules are
also included in unit-test coverage measurement when unit tests exercise them.
Scope#
Add at least one focused test for every changed library function or method.
Cover success paths, error paths, and important branches.
Keep tests fast and deterministic.
Mirror the package structure under
tests/unit_tests/.
The project target is at least 90% coverage. New code should stay close to full branch and line coverage.
Commands#
Run all unit tests:
pytest tests/unit_tests/
Run focused coverage for one module:
pytest tests/unit_tests/path/to/test_module.py \
--cov=simtools.path.to.module --cov-report=annotate:cov_annotate
Useful options:
pytest --durations=10 tests/unit_tests/
pytest --random-order tests/unit_tests/
pytest -n 4 --dist loadscope tests/unit_tests/
Test Design#
Reuse fixtures from
tests/unit_tests/conftest.pyandtests/conftest.pybefore adding new helpers.Keep mutable settings, simulator paths, and I/O handlers explicit in the test or module that needs them. Global autouse fixtures are reserved for harmless process-wide safety such as the matplotlib backend.
Prefer one parametrized test with descriptive case IDs for an invariant that has several inputs. Keep distinct error contracts and boundary branches as separate cases in the same table.
Every collected test must have an explicit oracle: an assertion, an
assert_*helper, apytest.raises/pytest.warnscontext, or a mock assertion. Assertions such asis not None,.called, or file existence alone are not sufficient when the returned value or file content is known.Do not make unit tests depend on checked-in, downloaded, or external files. When file parsing or writing is the behavior under test, generate the smallest valid input in
tmp_test_directorywithin the test.Keep file-format compatibility and resource-heavy checks in integration tests.
Mock only external programs (CORSIKA, sim_telarray, container engines, HTCondor), network access, model sources (through the shared model-reader fixtures), and time/uuid/randomness. Do not add patches of other
simtools.*internals (see Architecture Rules).Use
pytest.approx()for floating-point values.Use
astropy.tests.helper.assert_quantity_allclosefor astropy quantities.
Keep local fixtures close to the test module that uses them.
Coverage Workflow#
Coverage is a diagnostic tool, not the goal by itself. It includes application modules as well as library code. Use it to find missing branches after the semantic behavior is covered.
pytest --cov-branch --cov-report=html --cov-report=json tests/unit_tests/
Open the file htmlcov/index.html in your browser to see the coverage report.