How ToTest a 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

import biosimulant as biosim
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

import biosimulant as biosim
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:

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:

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.

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

Next steps