Skip to content
Migrating from Legacy Systems

Manufacturing Test Frameworks Compared

A comparison of manufacturing test frameworks (OpenHTF, pytest, OpenTAP, TestStand) with code examples, feature matrices, cost analysis, and guidance on.

JJulien Buteau
intermediate14 min readMarch 14, 2026

Four frameworks dominate manufacturing test automation: OpenHTF, pytest, OpenTAP, and NI TestStand. Each makes different tradeoffs between structure, flexibility, cost, and ecosystem. This guide compares them with real code, concrete metrics, and decision criteria so you can pick the right one.

Framework Overview

FrameworkLanguageLicenseOriginFocus
OpenHTFPythonApache 2.0 (free)GoogleManufacturing/production test
pytestPythonMIT (free)CommunitySoftware testing, adapted for hardware
OpenTAPC# / PythonMPL 2.0 (free)KeysightInstrument-heavy test automation
NI TestStandLabVIEW / C / PythonCommercial ($4,310/seat)NI (Emerson)Enterprise manufacturing test

Feature Comparison Matrix

FeatureOpenHTFpytestOpenTAPTestStand
Structured measurementsBuilt-in (name, value, limits, units)Manual (assert only)Plugin-basedBuilt-in
Serial number inputBuilt-in promptManualPluginBuilt-in
Operator UIBuilt-in web UINoneBuilt-in (GUI editor)Built-in (Sequence Editor)
Test sequencingPhase orderingFunction ordering (plugins)Step ordering (GUI)Sequence files (GUI)
Parallel DUTLimitedNative (pytest-xdist)NativeNative
Instrument driversPlugs (Python)Fixtures (Python)Plugins (C#/Python)NI drivers (LabVIEW)
Report formatProtobufJUnit XMLXML/JSONXML/database
Version controlGit (Python files)Git (Python files)Git (XML + code)Difficult (binary .seq files)
CI/CD integrationNative (Python)Native (Python)PossibleDifficult
Cross-platformLinux, macOS, WindowsLinux, macOS, WindowsLinux, WindowsWindows only
CommunitySmall (~640 stars)Massive (11K+ stars)Small (~200 stars)Large (NI forums)
Learning curveMediumLowMediumHigh
Annual cost (5 seats)$0$0$0~$21,550

The Same Test in Each Framework

A simple functional test: measure a 3.3V rail voltage, check it's within 3.2V to 3.4V.

OpenHTF

comparison/openhtf_example.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot


class DutPlug(htf.plugs.BasePlug):
    def setUp(self):
        self.voltage = 3.31  # Replace with instrument read

    def tearDown(self):
        pass


@htf.measures(
    htf.Measurement("rail_3v3")
    .in_range(3.2, 3.4)
    .with_units(units.VOLT),
)
@htf.plug(dut=DutPlug)
def test_power(test, dut):
    test.measurements.rail_3v3 = dut.voltage


def main():
    test = htf.Test(test_power, procedure_id="FCT-001", part_number="PCBA-100")
    with TofuPilot(test):
        test.execute(test_start=lambda: input("Serial: "))


if __name__ == "__main__":
    main()

Strengths: Measurements are structured data (name, value, limits, units). One line for TofuPilot integration. Operator gets a serial number prompt automatically.

pytest

comparison/pytest_example.py
import pytest
from tofupilot import TofuPilotClient


@pytest.fixture
def dut():
    connection = {"voltage": 3.31}  # Replace with real connection
    yield connection


def test_power_rail(dut):
    voltage = dut["voltage"]
    assert 3.2 <= voltage <= 3.4, f"3.3V rail: {voltage}V"

Strengths: Familiar to every Python developer. Huge plugin ecosystem. Flexible fixture system. Great for R&D and validation.

Weakness: Measurements are implicit (assert statements). No structured data for analytics without extra code.

OpenTAP

comparison/opentap_example.cs
// OpenTAP C# test step
using OpenTap;

[Display("Power Rail Test", Group: "FCT")]
public class PowerRailStep : TestStep
{
    [Display("Lower Limit")]
    public double LowerLimit { get; set; } = 3.2;

    [Display("Upper Limit")]
    public double UpperLimit { get; set; } = 3.4;

    public override void Run()
    {
        double voltage = 3.31; // Replace with instrument read
        Results.Publish("rail_3v3", new { Voltage = voltage });

        if (voltage < LowerLimit || voltage > UpperLimit)
            UpgradeVerdict(Verdict.Fail);
    }
}

Strengths: GUI step editor for non-programmers. Strong Keysight instrument integration. Plugin architecture for test plans.

Weakness: C# primary language (Python plugin exists but is secondary). Smaller community.

NI TestStand

TestStand uses a visual sequence editor. The equivalent test is a sequence file (.seq) with a "Numeric Limit Test" step configured via GUI: test value = voltage reading, low limit = 3.2, high limit = 3.4.

Strengths: Mature, enterprise-grade. Deep NI hardware integration. Built-in report generation, database logging, parallel execution.

Weakness: $4,310/seat/year. Windows only. Binary sequence files don't version-control well. Tied to NI ecosystem.

Cost Analysis

OpenHTFpytestOpenTAPTestStand
License (5 seats)$0$0$0$21,550/year
License (20 seats)$0$0$0$86,200/year
Runtime deploymentFreeFreeFreeAdditional runtime licenses
TrainingSelf-taught (docs)Self-taught (docs)Self-taught (docs)NI training courses ($2K+)
Vendor lock-inNoneNoneLow (Keysight-adjacent)High (NI ecosystem)
SupportCommunity (GitHub)Community (massive)Community + KeysightNI support contract

When to Use Each Framework

ScenarioBest ChoiceWhy
Production FCT, Python teamOpenHTFBuilt for manufacturing test, structured measurements, operator UI
R&D validation, firmware CIpytestFlexible, fast iteration, CI/CD native, huge ecosystem
Multi-vendor instruments, enterpriseOpenTAPPlugin architecture, GUI editor, Keysight integration
Existing NI hardware, large enterpriseTestStandDeep NI integration, enterprise support, existing infrastructure
Small team, budget-consciousOpenHTF or pytestZero license cost, Python ecosystem
Mixed (R&D + production)pytest + OpenHTFpytest for validation, OpenHTF for production

Migration Paths

From TestStand to OpenHTF

TestStand ConceptOpenHTF Equivalent
Sequence file (.seq)Python test script
Step typesPhase functions
Numeric Limit Test@htf.measures with .in_range()
String Value Test@htf.measures with .equals()
Step module (code)Plug class
Process modelTofuPilot integration
Report generationTofuPilot dashboard
UUT serial numbertest.execute(test_start=lambda: input("Serial: "))

From pytest to OpenHTF

pytest ConceptOpenHTF Equivalent
Test functionPhase function
FixturePlug
Assert statement@htf.measures with validators
conftest.pyPlug classes in shared module
JUnit XMLProtobuf output + TofuPilot

TofuPilot Integration

All four frameworks work with TofuPilot:

FrameworkIntegration MethodEffort
OpenHTFNative (with TofuPilot(test))1 line
pytestPython SDK (TofuPilotClient)~10 lines per test
OpenTAPREST API or Python SDKMedium
TestStandREST APIMedium

Decision Flowchart

  1. Do you have existing TestStand infrastructure? Yes, and it works well: stay with TestStand. Yes, but you want to migrate: move to OpenHTF.
  2. Are you building production tests? Yes: OpenHTF. It was built for this.
  3. Are you doing R&D or firmware validation? Yes: pytest. More flexible, better CI/CD.
  4. Do you need a GUI for non-programmers? Yes: OpenTAP. Visual step editor.
  5. Unsure? Start with pytest. It's the easiest to learn and you can always add OpenHTF for production later.

More Guides

Put this guide into practice