Note
Ophyd async is included on a provisional basis until the v1.0 release and may change API on minor release numbers before then
Write Tests for Devices#
Testing ophyd-async devices using tools like mocking, patching, and fixtures can become complicated very quickly. The library provides several utilities to make it easier.
Async Tests#
pytest-asyncio is required for async tests. It is should be included as a dev dependency of your project. Tests can either be decorated with @pytest.mark.asyncio
or the project can be automatically configured to detect async tests.
# pyproject.toml
[tool.pytest.ini_options]
...
asyncio_mode = "auto"
Sim Backend#
Ophyd devices initialized with a sim backend behave in a similar way to mocks, without requiring you to mock out all the dependencies and internals. The DeviceCollector
can initialize any number of devices, and their signals and sub-devices (recursively), with a sim backend.
@pytest.fixture
async def sim_sensor() -> demo.Sensor:
async with DeviceCollector(sim=True):
sim_sensor = demo.Sensor("SIM:SENSOR:")
# Signals connected here
assert sim_sensor.name == "sim_sensor"
return sim_sensor
Sim Utility Functions#
Sim signals behave as simply as possible, holding a sensible default value when initialized and retaining any value (in memory) to which they are set. This model breaks down in the case of read-only signals, which cannot be set because there is an expectation of some external device setting them in the real world. There is a utility function, set_sim_value
, to mock-set values for sim signals, including read-only ones.
async def test_sensor_reading_shows_value(sim_sensor: demo.Sensor):
# Check default value
assert (await sim_sensor.value.get_value()) == pytest.approx(0.0)
assert (await sim_sensor.read()) == {
"sim_sensor-value": {
"alarm_severity": 0,
"timestamp": ANY,
"value": 0.0,
}
}
# Check different value
set_sim_value(sim_sensor.value, 5.0)
assert (await sim_sensor.read()) == {
"sim_sensor-value": {
"alarm_severity": 0,
"timestamp": ANY,
"value": 5.0,
}
}
There is another utility function, set_sim_callback
, for hooking in logic when a sim value changes (e.g. because someone puts to it).
async def test_mover_stopped(sim_mover: demo.Mover):
callbacks = []
set_sim_callback(sim_mover.stop_, lambda r, v: callbacks.append(v))
assert callbacks == [None]
await sim_mover.stop()
assert callbacks == [None, None]