Testing
This document explains the testing strategy for simind-python-connector, which handles the challenge of testing code that depends on optional external dependencies (SIRF, STIR, SIMIND, and PyTomography) that may not be available in every environment.
Test Organization
Test Markers
Tests are organized using pytest markers to indicate their dependencies:
@pytest.mark.unit- Pure unit tests with no external dependencies (CI-friendly)@pytest.mark.requires_sirf- Tests that require SIRF to be installed@pytest.mark.requires_stir- Tests that require STIR Python to be installed@pytest.mark.requires_simind- Tests that require SIMIND command-line tool@pytest.mark.requires_pytomography- Tests that require PyTomography to be installed@pytest.mark.integration- Integration tests (may be slow)@pytest.mark.ci_skip- Tests to skip in CI environments
Automatic Dependency Detection
The test suite automatically detects available dependencies:
SIRF detection: Attempts to
import sirfSTIR detection: Attempts to
import stirandimport stirextraPyTomography detection: Attempts to
import pytomographySIMIND detection: Checks if
simindcommand is availableCI detection: Checks
CIorGITHUB_ACTIONSenvironment variables
Tests requiring unavailable dependencies are automatically skipped with informative messages.
Running Tests
Local Development (All Tests)
# Run all available tests
pytest
# Run only unit tests (fast)
pytest -m unit
# Run only SIRF-dependent tests
pytest -m requires_sirf
# Run with coverage
pytest --cov=simind_python_connector --cov-report=html
CI Environment (GitHub Actions)
The GitHub Actions workflow runs only CI-friendly tests:
# CI command (no SIRF/SIMIND dependencies)
pytest -m "not requires_sirf and not requires_stir and not requires_simind and not requires_pytomography and not ci_skip"
Docker Backend Isolation
Use the dedicated backend containers to validate connector separation and run one example per backend in isolated environments:
bash scripts/run_container_validation.sh
bash scripts/run_container_examples.sh
Run only selected groups:
bash scripts/run_container_validation.sh --only-core
bash scripts/run_container_validation.sh --only-pytomography
bash scripts/run_container_examples.sh --only-osem
Override target architecture (or let scripts auto-detect from SIMIND binary):
bash scripts/run_container_examples.sh --only-core --docker-platform linux/amd64
bash scripts/run_container_validation.sh --with-simind --docker-platform linux/amd64
To include SIMIND-dependent integration/example checks in the validation run:
bash scripts/run_container_validation.sh --with-simind
The container scripts check for a repo-local SIMIND executable at
./simind/simind before running SIMIND-dependent checks. If missing, they
skip those checks by default. Use --require-simind to fail fast instead.
input.smc remains packaged in simind_python_connector/configs and is not
part of the SIMIND runtime availability check.
SIMIND itself is not bundled with this package; install it separately from the official SIMIND site and manual:
https://www.msf.lu.se/en/research/simind-monte-carlo-program
https://www.msf.lu.se/en/research/simind-monte-carlo-program/manual
The container suite includes tests/test_container_library_isolation.py,
which asserts that each container can import only its target backend library.
When SIMIND mode is enabled, it also runs geometry-isolation diagnostics:
tests/test_geometry_isolation_forward_projection.py and
tests/test_osem_geometry_diagnostics.py.
Alternative CI Configuration
You can also use the dedicated CI pytest configuration:
# Using CI-specific config
pytest -c pytest-ci.ini
Test Categories
CI-Friendly Test Suites
The default CI job runs the tests that avoid heavyweight dependencies. These focus on:
Conversion maths –
tests/test_schneider_density.pyexercises the HU-to-density pipelines.Configuration & builders –
tests/test_simulation_config.pyandtests/test_acquisition_builder_unit.pyvalidate YAML/SMC handling plus Interfile header generation without a SIRF runtime.Connector behavior –
tests/test_python_connector.py,tests/test_connectors_base.py, andtests/test_connector_backend_separation.pycover the connector-first API without requiring reconstruction packages.Utility helpers –
tests/test_utils.py,tests/test_utils_small.py,tests/test_interfile_parser.py, andtests/test_interfile_numpy.pycover file parsing and small helper functions.Backend guards –
tests/test_backends.py,tests/test_marker_policy.py, andtests/test_examples_backend_separation.pyensure optional backend imports remain isolated.
Backend-Dependent Suites
Tests that construct real STIR/SIRF/PyTomography objects carry dependency markers and are skipped when those packages are unavailable. These include:
tests/test_native_adaptors.pyfor STIR/SIRF adaptor behavior.tests/test_pytomography_adaptor.pyfor PyTomography tensor and output adaptation.tests/test_arithmetic_operations.pyandtests/test_sirf_stir_utils.pyfor backend wrapper/helper behavior.
SIMIND and Container Diagnostics
SIMIND-dependent tests use requires_simind and are skipped unless the
simind command is available. Container and geometry diagnostics include:
tests/test_integration.pyfor full workflow checks.tests/test_container_library_isolation.pyfor backend container import isolation.tests/test_geometry_isolation_forward_projection.pyandtests/test_osem_geometry_diagnostics.pyfor backend geometry diagnostics.
Integration Test
tests/test_integration.py orchestrates a full example run and needs
external runtime dependencies. It is marked with dependency markers so it is
skipped in lightweight CI environments.
Configuration Files
pytest.ini (Local Development)
Runs all available tests based on detected dependencies
Includes verbose output and duration reporting
pytest-ci.ini (CI Environment)
Specifically filters out dependency-requiring tests
Optimized for GitHub Actions environment
tests/conftest.py
Configures pytest markers
Implements automatic dependency detection and test skipping
Handles CI environment detection
Adding New Tests
When adding new tests, use appropriate markers:
import pytest
@pytest.mark.unit
def test_pure_python_logic():
"""Test that doesn't need external dependencies."""
assert True
@pytest.mark.requires_sirf
def test_sirf_functionality():
"""Test that uses SIRF objects."""
from sirf.STIR import ImageData
# ... test code
@pytest.mark.requires_simind
def test_simind_execution():
"""Test that calls SIMIND command."""
# ... test code that runs simind
@pytest.mark.requires_pytomography
def test_pytomography_path():
"""Test that uses PyTomography APIs."""
# ... test code that imports pytomography
This ensures your tests will be properly categorized and run in the appropriate environments.
Continuous Integration
GitHub Actions is used to run tests automatically. The CI workflow:
Installs only basic Python dependencies (no SIRF/SIMIND)
Runs code quality checks (
ruff checkandruff format --check)Executes CI-friendly tests using dependency markers
Generates coverage reports for the tested code
Builds and validates the package
This approach ensures reliable CI while maintaining comprehensive test coverage for local development.