Getting Started

Utiliser pytest pour les tests matériels

Apprenez à utiliser pytest pour automatiser les tests matériels et envoyer les résultats à TofuPilot pour un suivi centralisé et des analyses.

JJulien Buteau
beginner10 min de lecture14 mars 2026

Comment utiliser pytest pour les tests matériels avec TofuPilot

pytest est le framework de test Python le plus populaire. Vous le connaissez déjà pour les tests logiciels. Vous pouvez aussi l'utiliser pour les tests matériels. Ce guide montre comment structurer les tests matériels avec pytest et envoyer les résultats à TofuPilot.

Pourquoi pytest pour le matériel

FonctionnalitéComment elle aide les tests matériels
FixturesConfigurer les instruments, alimentations, connexions
ParametrizeExécuter le même test sur plusieurs DUT ou configurations
MarkersÉtiqueter les tests par type (fonctionnel, sécurité, calibration)
AssertionsDéfinir les critères de réussite/échec pour les mesures
PluginsÉtendre avec du reporting personnalisé, exécution parallèle

pytest vous fournit le lanceur de tests. TofuPilot vous fournit la plateforme de données. Ensemble, ils remplacent les scripts ad-hoc et les fichiers Excel.

Test matériel de base avec pytest

test_power_rails.py
import pytestimport pyvisafrom tofupilot import TofuPilotClient# Fixtures pour la configuration des instruments@pytest.fixture(scope="session")def rm():    return pyvisa.ResourceManager()@pytest.fixture(scope="session")def dmm(rm):    inst = rm.open_resource("TCPIP::192.168.1.10::INSTR")    yield inst    inst.close()@pytest.fixture(scope="session")def psu(rm):    inst = rm.open_resource("TCPIP::192.168.1.11::INSTR")    inst.write("OUTP ON")    yield inst    inst.write("OUTP OFF")    inst.close()@pytest.fixture(scope="session")def tofupilot():    return TofuPilotClient()# Testsdef test_vcc_3v3(dmm, tofupilot):    voltage = float(dmm.query("MEAS:VOLT:DC?"))    assert 3.25 <= voltage <= 3.35, f"Rail 3,3 V hors spécification : {voltage} V"def test_vcc_1v8(dmm, tofupilot):    voltage = float(dmm.query("MEAS:VOLT:DC?"))    assert 1.75 <= voltage <= 1.85, f"Rail 1,8 V hors spécification : {voltage} V"def test_idle_current(dmm, tofupilot):    current = float(dmm.query("MEAS:CURR:DC?")) * 1000  # mA    assert 30 <= current <= 60, f"Courant de repos hors spécification : {current} mA"

Envoi des résultats pytest à TofuPilot

Option 1 : Envoi dans une fixture (recommandé)

Collectez les mesures pendant les tests et envoyez-les à la fin.

conftest.py
import pytestfrom tofupilot import TofuPilotClientclass TestCollector:    """Collecte les mesures pendant une session de test."""    def __init__(self):        self.measurements = []        self.steps = []        self.all_passed = True    def add_measurement(self, step_name, name, value, unit, limit_low=None, limit_high=None):        passed = True        if limit_low is not None and value < limit_low:            passed = False        if limit_high is not None and value > limit_high:            passed = False        if not passed:            self.all_passed = False        # Trouver ou créer l'étape        step = next((s for s in self.steps if s["name"] == step_name), None)        if not step:            step = {"name": step_name, "step_type": "measurement", "status": True, "measurements": []}            self.steps.append(step)        measurement = {"name": name, "value": value, "unit": unit}        if limit_low is not None:            measurement["limit_low"] = limit_low        if limit_high is not None:            measurement["limit_high"] = limit_high        step["measurements"].append(measurement)        if not passed:            step["status"] = False@pytest.fixture(scope="session")def collector():    return TestCollector()@pytest.fixture(scope="session", autouse=True)def upload_results(collector):    yield  # Exécuter tous les tests    # Envoyer après la fin de tous les tests    client = TofuPilotClient()    serial = input("Entrez le numéro de série du DUT : ") if not hasattr(collector, "serial") else collector.serial    client.create_run(        procedure_id="PYTEST-BOARD-FUNCTIONAL",        unit_under_test={"serial_number": serial},        run_passed=collector.all_passed,        steps=collector.steps,    )
test_board.py
def test_vcc_3v3(dmm, collector):    voltage = float(dmm.query("MEAS:VOLT:DC?"))    collector.add_measurement("Power Rails", "vcc_3v3", voltage, "V", limit_low=3.25, limit_high=3.35)    assert 3.25 <= voltage <= 3.35def test_vcc_1v8(dmm, collector):    voltage = float(dmm.query("MEAS:VOLT:DC?"))    collector.add_measurement("Power Rails", "vcc_1v8", voltage, "V", limit_low=1.75, limit_high=1.85)    assert 1.75 <= voltage <= 1.85def test_idle_current(dmm, collector):    current = float(dmm.query("MEAS:CURR:DC?")) * 1000    collector.add_measurement("Current Draw", "idle_current_ma", current, "mA", limit_low=30, limit_high=60)    assert 30 <= current <= 60

Option 2 : Plugin pytest

Écrivez un plugin pytest qui se branche sur les résultats de test.

conftest.py
import pytestfrom tofupilot import TofuPilotClientdef pytest_sessionfinish(session, exitstatus):    """Envoie les résultats après la fin de tous les tests."""    client = TofuPilotClient()    passed = exitstatus == 0    # Collecter les résultats de la session    steps = []    for item in session.items:        report = item.stash.get("report", None)        if report:            steps.append({                "name": item.name,                "step_type": "measurement",                "status": report.passed,                "measurements": item.stash.get("measurements", []),            })    client.create_run(        procedure_id="PYTEST-FUNCTIONAL",        unit_under_test={"serial_number": session.config.getoption("--serial", "UNKNOWN")},        run_passed=passed,        steps=steps,    )

Exécution des tests

terminal
# Exécuter tous les tests matérielspytest test_board.py -v# Exécuter avec un numéro de sériepytest test_board.py --serial UNIT-5501# Exécuter uniquement les tests de rails d'alimentationpytest test_board.py -k "vcc" -v# Exécuter avec des markerspytest test_board.py -m "safety" -v

Organiser les tests matériels avec pytest

Utiliser les markers pour les catégories de tests

test_board.py
import pytest@pytest.mark.safetydef test_hipot(hipot_tester, collector):    """Test de sécurité - doit réussir pour chaque unité."""    leakage = hipot_tester.run_test(1500)    collector.add_measurement("Safety", "hipot_leakage_ma", leakage, "mA", limit_high=5.0)    assert leakage < 5.0@pytest.mark.functionaldef test_communication(dut, collector):    """Test fonctionnel - vérifie le fonctionnement de base."""    response = dut.ping()    collector.add_measurement("Communication", "uart_ping", 1 if response else 0, "bool", limit_low=1)    assert response@pytest.mark.calibrationdef test_adc_accuracy(dut, collector):    """Test de calibration - vérifie la précision des mesures."""    error = dut.measure_adc_error()    collector.add_measurement("Calibration", "adc_error_pct", error, "%", limit_high=0.5)    assert error < 0.5

Utiliser parametrize pour les tests multi-canaux

test_multi_channel.py
import pytest@pytest.mark.parametrize("channel,expected_v,tolerance", [    (1, 3.3, 0.05),    (2, 1.8, 0.05),    (3, 5.0, 0.10),    (4, 12.0, 0.20),])def test_voltage_rail(dmm, collector, channel, expected_v, tolerance):    voltage = dmm.measure_channel(channel)    collector.add_measurement(        "Power Rails",        f"rail_ch{channel}_v",        voltage,        "V",        limit_low=expected_v - tolerance,        limit_high=expected_v + tolerance,    )    assert abs(voltage - expected_v) < tolerance

pytest vs. OpenHTF

FonctionnalitépytestOpenHTF
Courbe d'apprentissageFaible (la plupart des développeurs Python le connaissent)Moyenne (spécifique au matériel)
Fonctionnalités de test matérielGénéralisteConçu pour le matériel (phases, plugs, mesures)
Interface opérateurAucune intégréeInterface web intégrée
CommunautéTrès largePetite mais spécialisée
Intégration TofuPilotVia du code personnaliséCallback natif

Utilisez pytest quand votre équipe le connaît déjà et que vous voulez quelque chose de fonctionnel rapidement. Utilisez OpenHTF quand vous avez besoin du framework complet de test matériel avec interface opérateur et phases structurées.

Les deux envoient les données à TofuPilot de la même manière. Les données dans TofuPilot sont identiques quel que soit le framework qui les a générées.

Plus de guides

Mettez ce guide en pratique