Testing experiments is critical to ensure reliable measurements. PLESTY provides mock devices and contract gates to validate experiment behavior without real hardware.
Using mock devices
Create mock device instances for testing:
from plesty.lib.device import BaseDeviceSyncModel
class MockLaser(BaseDeviceSyncModel):
"""Mock laser device for testing."""
def __init__(self, main=None):
super().__init__(main=main)
self._connected = False
self._power = 0.0
def connect(self) -> bool:
self._connected = True
return True
def disconnect(self) -> None:
self._connected = False
def _write_(self, key: str, value: object) -> None:
if key == "power":
self._power = float(value)
def _query_(self, key: str) -> object:
if key == "power":
return self._power
raise ValueError(f"Unknown key: {key}")
def identity(self) -> str:
return "MockLaser"
def check_errors(self) -> list[str]:
return []
def check_operatability(self) -> bool:
return self._connected
Testing with CompositeDevice
def test_power_sweep():
# Arrange
laser = MockLaser()
laser.connect()
composite = CompositeDevice(devices={"laser": laser})
experiment = PowerSweep(composite)
# Act
result = experiment.run(current_range=[0.0, 1.0, 2.0])
# Assert
assert len(result["measurements"]) == 3
assert result["measurements"][0]["current"] == 0.0
Gate e1: Experiment Contract
Gate e1 validates that your experiment implements the required contract:
- Experiment class exists and follows conventions
- Required methods are implemented
- Device interaction follows the expected pattern
Gate e2: Experiment Persistence
Gate e2 enforces that experiments use save_result() for data persistence, not ad-hoc file writing or database connections.
# Checks:
# - All data persistence goes through save_result
# - No direct file I/O for measurement data
# - No database connections in experiment code
Running experiment tests
# Run all tests including experiment tests
uv run pytest
# Run experiment-specific tests
uv run pytest -k "experiment"
# Run the full check suite (includes e1, e2)
uv run plesty check
Best practices
- Test each measurement workflow independently
- Use mock devices to simulate edge cases (disconnection, errors)
- Verify that
save_resultis called with the expected data - Test error handling and reconnection logic
Next steps
- Learn about quality gates in detail
- Set up the CI pipeline