Skip to content

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-TTY

Expected: 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 dut

test_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 against test_config, with log_vars to record the measurement.
  • test_status_led_gpiof3ts_assert to emit a sub-result.
  • test_firmware_version — a plain pytest assertion (no f3ts metadata needed).