pytest-native reference #

TesterKit is a hardware test platform; pytest is its primary test-runner integration. The bundled pytest plugin slots into a stock pytest install with zero configuration — every pytest concept (collection, fixtures, markers, plugins, conftest.py, command-line flags) works unchanged. This page is the map of what pytest gives you natively and what the plugin layers on top. For the plugin's own surface see TesterKit fixtures and TesterKit markers. Other runner integrations (OpenHTF, LabVIEW / TestStand via the results API) live under Integrations.

Collection #

The bundled pytest plugin uses pytest's default collection — no custom file collectors.

ConventionDefault
Test filestests/test_*.py or tests/*_test.py
Test classesclasses named Test* (no __init__)
Test functions / methodsfunctions named test_*
Overridestandard pytest python_files / python_classes / python_functions in pyproject.toml

What TesterKit adds at collection time:

  • A pytest_collection_modifyitems hook merges per-test sidecar YAML (tests/test_<module>.yaml) into each item's marker set. This expands testerkit_sweeps into one pytest case per row exactly as if you had written @pytest.mark.parametrize — pytest still owns the case multiplication.
  • Profiles (--test-profile=<name>) add pytest.mark.skip to items they exclude. The selection is visible in pytest --collect-only -q.

The sidecar is recursive: top-level keys apply to every test in the file; tests: { ClassName: { ... } } scopes per class; tests: { ClassName: { tests: { test_method: { ... } } } } scopes per method. See Sidecar configuration.

Fixtures #

pytest's fixture model is unchanged.

  • All four scopes work. function, class, module, session — choose the one that matches your resource cost.
  • Resolution by name. Take a fixture in the test signature; pytest resolves it from the nearest conftest.py upward, then from registered plugins (TesterKit included).
  • Yield fixtures, finalizers, request injection all work as pytest documents them.
  • autouse=True works. TesterKit uses an autouse-session fixture internally so every test records measurements without taking a recording fixture as an argument.

You can write your own fixtures in conftest.py alongside TesterKit's. A common pattern is a project-local uut factory wrapping the TesterKit uut session fixture, or a per-class hardware-setup fixture that takes instruments as a dependency.

The fixtures the TesterKit plugin contributes are documented in TesterKit fixtures.

Markers #

pytest's marker mechanism is unchanged. All of these work on TesterKit tests as documented in the pytest manual:

MarkerUse
@pytest.mark.parametrizeGenerate one pytest case per row. Stack to cross-product.
@pytest.mark.skip / @pytest.mark.skipifSkip the test (with optional condition).
@pytest.mark.xfailMark as expected-to-fail; surfaces as XFAIL / XPASS.
@pytest.mark.usefixtures("a", "b")Require fixtures without taking them in the signature.
@pytest.mark.filterwarningsPer-test warning filters.
Custom markers via pytest.ini / pyproject.tomlRegister and filter with -m.

The seven @pytest.mark.testerkit_* markers TesterKit adds are registered the same way as native markers and stack with them (with one constraint — see the no-stacking rule for TesterKit markers). See TesterKit markers for full details.

@pytest.mark.parametrize and @pytest.mark.testerkit_sweeps interoperate: both feed the same inputs lane on the vector row (query with FieldRef.input(name)).

conftest.py #

Works as pytest documents it. Place a conftest.py in tests/ (or a subdirectory) for fixtures and hooks that apply to that scope. Common uses with TesterKit:

  • Project-local fixtures that wrap or extend TesterKit's (a typed uut accessor, a per-class measurement helper).
  • pytest_addoption for project-specific CLI flags.
  • pytest_collection_modifyitems for project-specific item filtering. (TesterKit's hook is in the plugin and runs alongside, not instead of, yours.)
  • pytest_runtest_setup / _teardown for per-test setup beyond what fixtures express.

The TesterKit plugin loads via the standard pytest entry-point mechanism — no conftest.py manipulation needed.

Command-line flags #

All pytest flags work. The ones that matter most for hardware test work:

FlagPurpose
-k "expr"Run tests whose nodeid matches the substring expression.
-m "marker"Run tests whose markers match the boolean expression.
-x / --maxfail=NStop after the first failure / Nth failure.
--lf / --ffRun last-failed / failed-first (uses pytest's cache).
--collect-only -qShow what would run without running.
-v / -qVerbose / quiet.
--tb=short / --tb=line / --tb=noTraceback style.
-p no:pluginDisable a specific plugin.
--coAlias for --collect-only.

TesterKit adds the flags below.

FlagTypeDefaultDescription
--uut-serialtext'UUT001'UUT serial number
--uut-serialstextPer-site UUT serials: 0=SN1,1=SN2 (by index) or left=SN1,right=SN2 (by name)
--sitetextPhysical fixture site for this single-process run (e.g. 0, 1, or a named site like left). Use this when running a single UUT against a specific position in a multi-site fixture so the run records which site was exercised. Multi-site orchestration uses --uut-serials instead — supplying both is an error.
--uut-part-numbertextUUT part number
--uut-revisiontextUUT revision
--uut-lot-numbertextUUT lot/batch number (mirrors TESTERKIT_UUT_LOT_NUMBER env var)
--stationtextStation ID or YAML path. Bare id looks up stations/<id>.yaml; a value with / or .yaml/.yml is used as an explicit path. When unset, the resolver tries hostname auto-match against stations/*.yaml hostname: fields, then falls back to ProjectConfig.default_station.
--operatortextOperator name
--data-dirtextresolved at runtimeDirectory for Parquet results (default: platform data dir)
--parttextPart ID or YAML path. Bare id looks up parts/<id>.yaml; a value with / or .yaml/.yml is used as an explicit path.
--guardbandtext'0'Default guardband percentage
--mock-instrumentsflagFalseUse mock instruments instead of real hardware. Resolution: this flag > TESTERKIT_MOCK_INSTRUMENTS env var > testerkit.yaml mock_instruments: > false.
--no-mock-instrumentsflag (inverse)TrueUse real hardware (overrides TESTERKIT_MOCK_INSTRUMENTS env and testerkit.yaml mock_instruments: true).
--fixturetextFixture ID or YAML path. Bare id looks up fixtures/<id>.yaml; a value with / or .yaml/.yml is used as an explicit path. When unset, the resolver tries the active profile's fixture: field, then ProjectConfig.default_fixture, then the single-file fallback in fixtures/.
--test-phasetextTest phase (development, validation, characterization, production). Defaults to development; a non-development phase is demoted to development on a dirty git tree or under --mock-instruments.
--strict-traceabilityflagFalseFail tests whose measurements lack required traceability fields (run_id, step_name, and spec_ref/uut_pin when a spec is active).
--test-profiletextresolved at runtimeNamed profile from testerkit.yaml profiles: (overrides vectors, limits, markers, and filter for the session).
--no-test-profileflagFalseSkip profile resolution. Use when profiles are declared but you want to run with bare project defaults (ad-hoc runs).

Plus dynamic flags generated from testerkit.yaml: every profiles[*].facets: key becomes --<facet-key> and every required_inputs: key becomes --<required-input-key>. See how-to/profiles.md for the resolution chain (CLI flag → env var → profile binding → default_*).

Plugins that interact with TesterKit #

PluginNotes
pytest-rerunfailuresPowers @pytest.mark.testerkit_retry. Install it if you use the marker; the translation happens in the plugin.
pytest-xdistParallel execution. Generally not appropriate for hardware tests on a single bench (instruments aren't reentrant). Fine for mock-only suites and CI lint passes.
pytest-covCode coverage. Works unchanged on test files; collects coverage on the test code, not on instrument drivers behind hardware.
pytest-html / pytest-json-reportIndependent of TesterKit's own event log + parquet output. Run them alongside if you want pytest-flavored reports too.

Discovery vs activation #

Two things to keep separate:

  • Discovery is pytest's: what tests/test_*.py files match, what items they expose. TesterKit has no opinion here.
  • Activation is TesterKit's: which station, part, fixture, profile is loaded for the session. Driven by the CLI flags above (or by default_station: / default_profile: in testerkit.yaml).

A test that runs on a bringup tier with no station YAML and a test that runs on a factory tier with full traceability are collected identically. The activation context decides what fixtures resolve to and what limits verify finds.

See also #

Related quadrants: