Starter Test Plan
A complete, runnable plan for a fictional Widget Controller Board (WCB-1). Its interface.py is a mock that returns deterministic values, so the whole plan passes offline with no hardware — copy it and replace the mock with real hardware control.
The full source lives in examples/starter_plan/ in the repository.
Run it
bash
cd examples/starter_plan
pytest -p f3ts # interactive terminal
pytest -p f3ts --skip-summary # piped / CI / non-TTYExpected: 4 passed, with Test ID sections for the rails, an f3ts_assert sub-result for the GPIO check, and (in a terminal) the summary table. See Running Locally for the gotchas.
config.yml
yaml
test_name: "widget-controller-board"
runner_settings:
stop_at_first_failure: false
autorun: false
test_cases:
test_5v0_rail:
test_id: "1.1"
description: "5V0 rail within tolerance"
error_code: 101
error_msg: "5V0 rail out of range"
min_limit: 4.90
max_limit: 5.10
test_3v3_rail:
test_id: "1.2"
description: "3V3 rail within tolerance"
error_code: 102
error_msg: "3V3 rail out of range"
min_limit: 3.20
max_limit: 3.40
test_status_led_gpio:
test_id: "2.1"
description: "Status LED GPIO readback"
error_code: 201
error_msg: "Status LED did not read back high"interface.py
python
"""Mock interface for the fictional Widget Controller Board (WCB-1)."""
import logging
logger = logging.getLogger(__name__)
class WidgetInterface:
"""Deterministic stand-in for the device under test (DUT)."""
_RAILS = {"5v0": 5.01, "3v3": 3.31, "1v8": 1.79}
def __init__(self):
self._open = True
logger.info("WidgetInterface opened")
def measure_rail(self, name: str) -> float:
return self._RAILS[name]
def read_gpio(self, pin: str) -> int:
return 1
def firmware_version(self) -> str:
return "1.2.3"
def close(self):
self._open = False
logger.info("WidgetInterface closed")conftest.py
python
import logging
import pytest
from interface import WidgetInterface
logger = logging.getLogger(__name__)
@pytest.fixture(scope="session")
def interface(request):
dut = WidgetInterface()
def teardown():
dut.close()
request.addfinalizer(teardown)
return duttest_plan.py
python
from pytest_f3ts.utils import log_vars
def test_5v0_rail(interface, test_config, record_property):
meas = interface.measure_rail("5v0")
log_vars(record_property)
assert test_config.min_limit <= meas <= test_config.max_limit
def test_3v3_rail(interface, test_config, record_property):
meas = interface.measure_rail("3v3")
log_vars(record_property)
assert test_config.min_limit <= meas <= test_config.max_limit
def test_status_led_gpio(interface, f3ts_assert):
level = interface.read_gpio("status_led")
f3ts_assert(
assert_value=(level == 1),
meas=level,
description="Status LED reads high",
)
def test_firmware_version(interface):
assert interface.firmware_version() == "1.2.3"What each pattern shows
test_5v0_rail/test_3v3_rail— limit checks againsttest_config, withlog_varsto record the measurement.test_status_led_gpio—f3ts_assertto emit a sub-result.test_firmware_version— a plain pytest assertion (no f3ts metadata needed).