<!-- Source: https://docs.biosimulant.com/how-to/test-biomodule -->

# How to Test a BioModule

Current-kernel modules are plain Python objects. Test them directly with pytest, then add a small integration test with a real `BioWorld`.

## Unit tests

```python

from src.my_module import PopulationCounter

def window_context():
    return biosim.ExecutionContext(
        policy=biosim.ExecutionPolicy.EACH_WINDOW,
        run_start=0.0,
        run_end=1.0,
        window_start=0.0,
        window_end=1.0,
    )

def test_ports_are_declared():
    mod = PopulationCounter(initial_count=100)
    assert "deaths" in mod.inputs()
    assert "population_state" in mod.outputs()

def test_execute_updates_state():
    mod = PopulationCounter(initial_count=100, growth_rate=0.1)
    mod.execute({}, context=window_context())
    snap = mod.snapshot()
    assert snap["count"] == 110

def test_execute_accepts_typed_signal():
    mod = PopulationCounter(initial_count=100, growth_rate=0.0)
    inputs = {
        "deaths": biosim.ScalarSignal(
            source="stim",
            name="deaths",
            value=5,
            emitted_at=0.0,
            spec=biosim.SignalSpec.scalar(dtype="int64"),
        )
    }
    mod.execute(inputs, context=window_context())
    assert mod.snapshot()["count"] == 95

def test_snapshot_round_trip():
    mod = PopulationCounter(initial_count=50, growth_rate=0.1)
    mod.execute({}, context=window_context())
    snap = mod.snapshot()

    restored = PopulationCounter(initial_count=0, growth_rate=0.0)
    restored.restore(snap)
    assert restored.snapshot() == snap
```

## Integration test with BioWorld

```python

from src.my_module import PopulationCounter

def test_world_run_collects_outputs():
    world = biosim.BioWorld(communication_step=1.0)
    builder = biosim.WiringBuilder(world)
    builder.add("population", PopulationCounter(initial_count=50, growth_rate=0.05))
    builder.apply()

    world.run(duration=5.0)

    outputs = world.get_outputs("population")
    assert "population_state" in outputs
```

For canonical modules, test policy and invocation count through BioWorld:

```python
class CountingPredictor(biosim.BioModule):
    execution_policy = biosim.ExecutionPolicy.ONCE_BEFORE_RUN

    def __init__(self):
        self.calls = 0

    def outputs(self):
        return {"score": biosim.SignalSpec.scalar(dtype="float64")}

    def execute(self, inputs, *, context):
        self.calls += 1
        return {"score": 0.5}

def test_predictor_runs_once_per_run():
    predictor = CountingPredictor()
    world = biosim.BioWorld(communication_step=0.1)
    world.add_biomodule("predictor", predictor)

    world.run(duration=1.0)

    assert predictor.calls == 1
    assert world.get_outputs("predictor")["score"].emitted_at == 0.0
```

## What to verify

- declared ports match the manifest `io` block
- emitted signals use the correct typed signal class
- canonical policy, dependency readiness, invocation count, and boundary
  timestamp are correct
- `snapshot()` / `restore()` round-trip state correctly
- `visualize()` returns transport-safe JSON data when present

## Snapshot regression checks

For refactors that should preserve behavior, capture a normalized output
snapshot before and after the change. The script lives in the `biosim` source
repository. `pip install biosimulant` does not include it, so run it from a
checkout:

```bash
git clone https://github.com/Biosimulant/biosim.git
cd biosim
python scripts/snapshot_biomodule_outputs.py \
  path/to/model-or-lab \
  --duration 1.0 \
  --output snapshots/baseline.json
```

After the change, run it again with `--compare snapshots/baseline.json`. The
script fails if the new snapshot differs.

The snapshot includes declared input/output specs, emitted signals, module or
world state, and visual JSON. Dependency installation is opt-in with
`--install-deps`, which keeps heavyweight simulator and ML stacks explicit.

**Info:**

  Prefer testing snapshot/restore over relying on `reset()`. The world does not provide a kernel-level `reset()` API.

## Next steps

- [Create a BioModule](/how-to/create-biomodule)
- [Use a BioWorld](/how-to/use-bioworld)
