Building Robust Tests
What This Document Is For
New to testing? This guide helps you write tests for PROTEUS code. Tests are small programs that check if your code works correctly. When you change code, tests catch bugs before they reach production.
Key concept: For most code changes, you only need unit tests (fast, isolated tests). Integration tests are for advanced scenarios involving multiple physics modules working together.
For test markers and CI pipelines, see Test Categorization. For coverage analysis and troubleshooting, see Test Infrastructure.
Quick Start: Writing Your First Test
- Create test file: For
src/proteus/utils/helper.py, createtests/utils/test_helper.py - Add a test function: Start with
test_prefix, add@pytest.mark.unitmarker - Run it:
pytest tests/utils/test_helper.py -v - Check coverage:
pytest --cov=src tests/utils/
import pytest
from proteus.utils.helper import my_function
@pytest.mark.unit
def test_my_function_basic():
"""Test that my_function returns expected value."""
result = my_function(input_value=10)
assert result == pytest.approx(expected_value, rel=1e-5)
Developer Workflow
- Open source: The file under test (e.g.,
src/proteus/utils/helper.py) - Open destination: The test file (e.g.,
tests/utils/test_helper.py) - Open fixtures:
tests/conftest.pyfor available fixtures - Write tests: Use the prompts below if using AI assistance
- Run and verify:
pytest -m unitthenruff format tests/
Master Prompt (Unit Tests)
Copy into the chat when generating unit tests:
Act as a Senior Scientific Software Engineer for PROTEUS.
I need robust unit tests for the open file. Follow these strict guidelines:
- Architecture: Mirror the source. If testing
class Convection, createclass TestConvection. File:tests/<module>/test_<filename>.pyforsrc/proteus/<module>/<filename>.py.- Mocking: This is a unit test. Aggressively mock heavy physics (SOCRATES, AGNI) and I/O with
unittest.mock. Tests must run in <100 ms.- Precision: Use
pytest.approx(expected, rel=1e-5)for all float comparisons. Never use==for floats.- Physics: Use physically valid inputs (e.g. T > 0 K, P > 0) unless testing error handling.
- Coverage: Aim for high coverage; include edge cases (None, empty arrays, invalid values where relevant).
- Style: Use
@pytest.mark.parametrizefor data-driven tests. Add a brief docstring per test describing the scenario. Use@pytest.mark.unit.- Format: Run
ruff formaton test files before committing.Generate the tests now.
Integration Prompt (Standard Configuration)
Use when adding or extending integration tests (e.g. ARAGOG+AGNI+CALLIOPE+ZEPHYRUS+MORS):
Act as a Senior Scientific Software Engineer for PROTEUS.
I need an integration test for the Standard Configuration (e.g. test_std_config.py or multi-module coupling).
- Scope: Test coupling of the relevant modules (ARAGOG, AGNI, CALLIOPE, ZEPHYRUS, MORS as needed).
- Mocking: Do not mock internal physics between these modules. Mock only external I/O (e.g. network downloads) with
unittest.mock.- Config: Use fixtures from
tests/conftest.pyandtests/integration/conftest.py(e.g.intermediate_params, config paths).- Verification: Stable evolution (multiple timesteps, no crash/NaN); energy and mass conservation with stated tolerances; feedback checks (e.g. T_surf ↔ outgassing ↔ atmos_mass).
- Marker: Use
@pytest.mark.integration(and@pytest.mark.slowif long-running).Generate the integration test skeleton.
See Also
- Test Categorization — Markers, CI pipeline, fixtures
- Test Infrastructure — Layout, coverage, reusable quality gate
- Docker CI Architecture — Docker image, CI pipelines
- .github/copilot-instructions.md — Test commands and coverage thresholds