Tests¶
The test suite lives in the tests/ directory at the repository root. Tests use pytest.
Running tests¶
# Run the full suite
pytest
# Run a specific test file
pytest tests/test_pipelines.py
# Run a specific test class or function
pytest tests/test_pipelines.py::TestFerreUtilsHelpers::test_get_ferre_spectrum_name
# With coverage
pytest --cov=astra
In-memory database¶
Tests that interact with the database set ASTRA_DATABASE_PATH to :memory: before importing any Astra modules. This is done at the top of each test file:
import os
os.environ["ASTRA_DATABASE_PATH"] = ":memory:"
This ensures that every test run starts with a fresh, empty SQLite database. No external database server is needed.
When a test needs tables to exist, it creates them explicitly:
from astra.models.source import Source
from astra.models.spectrum import Spectrum
from astra.models.pipeline import PipelineOutputMixin
class Dummy(PipelineOutputMixin):
pass
for model in (Source, Spectrum, Dummy):
model.create_table()
Test structure¶
The test files cover different aspects of the codebase:
File |
What it tests |
|---|---|
|
ORM model behaviour: unique constraints, upsert logic, version handling. |
|
Pipeline utility functions (FERRE helpers, ASPCAP helpers, spectral resampling, The Payne utilities, SLAM wavelength tools, SnowWhite helpers). Uses |
|
Custom Peewee field types ( |
|
Glossary consistency checks. |
|
|
|
BitField flag definitions on pipeline models. |
|
File path generation on spectrum models. |
|
|
|
|
|
Utility functions (version encoding, path expansion, etc.). |
|
|
Loading modules without side effects¶
Some pipeline modules have heavy imports (TensorFlow, large data files) or trigger database connections through their __init__.py. The test suite avoids this by loading individual .py files directly using importlib:
import importlib.util
def _load_module_from_file(name, path):
"""Load a Python module directly from a file path."""
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
# Load just the utils module from a pipeline, skipping __init__.py
mod = _load_module_from_file(
"aspcap_utils",
os.path.join(_SRC, "astra", "pipelines", "aspcap", "utils.py"),
)
This pattern is used extensively in test_pipelines.py to test pipeline helper functions in isolation.
Writing new tests¶
When adding a test for a new pipeline or model:
Set
os.environ["ASTRA_DATABASE_PATH"] = ":memory:"at the top of the file, before anyastraimports.Create any needed tables in a fixture or at the start of the test.
Use
_load_module_from_fileif you only need to test utility functions and want to avoid heavy dependencies.For integration tests that exercise the
@taskdecorator, create a dummy pipeline model, define a small@taskfunction, and pass a handful of test spectra through it (seetest_pipeline_replace_on_conflictintest_models.pyfor an example).