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:

  1. All tests in Slicer: Use Slicer’s test framework exclusively

  2. All tests standalone: Mock everything, run with pytest

  3. 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