# OpenHTF vs pytest for Hardware Testing
URL: /guides/openhtf-vs-pytest
A practical comparison of OpenHTF and pytest for testing physical products — execution model, measurements vs assertions, fixtures vs plugs, operator interaction, output records — and when to use each or both.
Teams that already write Python ask this first. Both are Python, both are open source, both run "tests". They are built for different jobs.
**pytest** runs many independent test functions against software and reports pass/fail per function. **OpenHTF** runs one physical unit through an ordered sequence of steps and records measured values with limits, per unit, for the life of the product. Use pytest to test your code (including your plugs); use OpenHTF to test your hardware.
OpenHTF
pytest
Unit of execution
One DUT through one `Test` (a run)
A collection of `test_*` functions
Ordering
Explicit, sequential; flow control via `PhaseResult`, groups, checkpoints, branches
Unordered by design; ordering needs plugins
Result granularity
Measurements with value, limits, unit, marginal flag; phase outcome; test outcome
Assertion pass/fail; values are lost unless printed
Hardware access
Plugs: one instance per run, `tearDown` guaranteed, injected by decorator
Fixtures: scoped setup/teardown, injected by name
Operator
Built-in Operator UI and prompts
None; needs custom code
Serial-number tracking
`dut_id` on every record, prompted or set programmatically
None built in
Output
Structured test record → JSON, database via callbacks
Console, JUnit XML, plugins
Retries / conditional steps
`REPEAT`, `repeat_limit`, `run_if`, `BranchSequence`, `Subtest`
`pytest-rerunfailures`, `skipif`
Background sampling
Monitors
None
Typical consumer of results
Manufacturing, quality, repair — per unit, over months
Developers, CI — per commit
The value `v` exists only in the failure message. Six months later, "is Vout drifting upward across the lot?" cannot be answered from pytest output.
Every run stores `vout = 5.012 V, limits 4.95–5.05, PASS`. Yield, Cpk and drift are queries over the records — see Manufacturing Test Analytics.
pytest fixtures and OpenHTF plugs both manage setup and teardown of shared resources. Plugs add: a logger wired into the record, a `tearDown` that always runs after the run, state shared across every phase of one run by construction, and configuration binding for per-station addresses. Fixtures are more flexible in scope (function, module, session); plugs are always per run, which is what a station wants.
A production test asks a human to scan a barcode, press a button, read an LED. pytest has no concept of this. OpenHTF's `UserInput` plug and Operator UI make it a one-liner — see Device Under Test.
Hardware tests have order: power on before measuring, calibrate before burn-in, release the fixture last. pytest deliberately does not guarantee order. OpenHTF's phase groups, checkpoints and subtests exist for exactly this.
**Unit-testing your plugs and helpers.** Mock the instrument, assert the SCPI strings. pytest, plain and simple.
**Firmware/software on the DUT that you can test without a fixture** — protocol parsers, CLI tools.
**CI on every commit** with hundreds of quick checks and rich failure diffs.
Many teams do both: pytest in CI for the test code, OpenHTF on the station for the product.
TofuPilot runs pytest suites on stations as well, mapping each `test_*` function to a phase and promoting `assert lo <= x <= hi, "label"` to a measurement — a pragmatic path when a team wants OpenHTF-style records from an existing pytest suite. See Pytest on TofuPilot. The structural advantages above still favour OpenHTF for tests written from scratch for a production line.