ADR-008: Testing Strategy
Status
Accepted
Context
Testing a Slicer extension presents unique challenges:
Some code requires Slicer runtime (VTK, Qt, MRML)
Event handling tests need actual Qt events
UI tests require widget instantiation
CI environments don’t have Slicer installed
Testing approaches considered:
All tests in Slicer: Use Slicer’s test framework exclusively
All tests standalone: Mock everything, run with pytest
Hybrid: Unit tests standalone, integration tests in Slicer
Decision
Use a hybrid testing strategy:
Unit Tests (pytest, CI-compatible)
Pure Python tests that don’t require Slicer:
MouseMaster/Testing/Python/
├── conftest.py # Fixtures
├── test_mouse_profile.py # Profile dataclass tests
├── test_preset_manager.py # Preset serialization tests
└── test_fixtures/
├── valid_preset.json
└── invalid_preset.json
What to test:
MouseProfile dataclass validation
Preset serialization/deserialization
JSON schema validation
Configuration utilities
Platform adapter logic (mocked platform)
Run with:
uv run pytest MouseMaster/Testing/Python/ -v
Integration Tests (Slicer-based)
Tests requiring Slicer runtime:
class MouseMasterTest(ScriptedLoadableModuleTest):
def test_module_loads(self):
"""Test module can be loaded."""
self.assertIsNotNone(slicer.modules.mousemaster)
def test_event_handler_installed(self):
"""Test event handler is installed on app."""
# Check event filter is active
pass
def test_button_mapping_works(self):
"""Test button press triggers mapped action."""
# Simulate button press, verify action
pass
Run in Slicer:
import MouseMaster
test = MouseMaster.MouseMasterTest()
test.runTest()
Manual Tests (documented procedures)
Complex scenarios documented in docs/TESTING.md:
ID |
Test |
Procedure |
|---|---|---|
MT-001 |
Basic button mapping |
1. Select mouse, 2. Map back button to undo, 3. Press back, 4. Verify undo |
MT-002 |
Preset persistence |
1. Create preset, 2. Restart Slicer, 3. Verify preset loaded |
MT-003 |
Cross-platform |
Run MT-001 on Windows, macOS, Linux |
CI Pipeline
GitHub Actions workflow:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv run mypy MouseMaster/MouseMasterLib/
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: uv run pytest MouseMaster/Testing/Python/ -v
Consequences
Positive
Fast unit tests run in CI on every PR
Integration tests verify actual Slicer behavior
Manual tests document complex user scenarios
Clear separation of test types
Developers can run unit tests without Slicer
Negative
Two test frameworks to maintain
Integration tests require manual trigger
Some code paths only tested in Slicer
Neutral
Need good mocking for unit tests
Manual tests require documentation discipline
References
pytest: https://docs.pytest.org/
Slicer Testing: https://slicer.readthedocs.io/en/latest/developer_guide/modules/testing.html
ADR-001: Event handler testing considerations