OpenHTF et pytest sont les deux frameworks Python les plus courants pour l'automatisation des tests matériels. Ils résolvent des problèmes différents. OpenHTF a été conçu par Google pour les tests de fabrication. pytest a été conçu pour les tests logiciels et adapté par les équipes matérielles. Ce guide les compare côte à côte avec du vrai code pour vous aider à faire le bon choix.
Comparaison des fonctionnalités
| Fonctionnalité | OpenHTF | pytest |
|---|---|---|
| Conçu pour | Tests de fabrication/production | Tests logiciels (adapté pour le matériel) |
| Structure de test | Phases (séquence ordonnée) | Fonctions (non ordonnées par défaut) |
| Mesures | Intégrées : nom, valeur, limites, unités | Manuelles : assertions ou fixtures personnalisées |
| Saisie du numéro de série | Intégrée (invite opérateur) | Implémentation manuelle |
| Interface opérateur | Interface web intégrée | Aucune (tierce partie ou personnalisée) |
| Cycle de vie des instruments | Plugs (setup/teardown automatiques) | Fixtures (similaires, plus flexibles) |
| Pièces jointes | Intégrées (fichiers, images, logs) | Manuelles (sauvegarde sur disque ou plugin personnalisé) |
| Rapport de test | Sortie protobuf structurée | XML JUnit, plugins personnalisés |
| Test DUT en parallèle | Limité (exécuteur de test unique) | Natif (pytest-xdist) |
| Taille de la communauté | Petite (~640 étoiles GitHub) | Massive (11 000+ étoiles, vaste écosystème) |
| Écosystème de plugins | Minimal | Des milliers de plugins |
| Courbe d'apprentissage | Plus raide (moins de documentation) | Plus douce (documentation extensive) |
| Intégration TofuPilot | Native (1 ligne de code) | Via SDK Python |
Le même test dans les deux frameworks
Voici un test fonctionnel qui vérifie l'alimentation et l'interface de communication d'un PCBA, écrit dans les deux frameworks.
Version OpenHTF
import openhtf as htf
from openhtf.plugs import BasePlug
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
class DutPlug(BasePlug):
"""Gère le cycle de vie de la connexion au DUT."""
def setUp(self):
self.connected = True # Remplacer par une vraie connexion
def read_voltage(self) -> float:
return 3.31 # Remplacer par une lecture instrument
def query_firmware(self) -> str:
return "2.1.0" # Remplacer par une requête au DUT
def tearDown(self):
self.connected = False
@htf.measures(
htf.Measurement("rail_3v3")
.in_range(3.2, 3.4)
.with_units(units.VOLT)
.doc("Tension du rail d'alimentation 3,3 V"),
)
@htf.PhaseOptions(timeout_s=10)
@htf.plug(dut=DutPlug)
def test_power(test, dut):
test.measurements.rail_3v3 = dut.read_voltage()
@htf.measures(
htf.Measurement("firmware_version")
.equals("2.1.0")
.doc("Chaîne de version firmware attendue"),
)
@htf.PhaseOptions(timeout_s=5)
@htf.plug(dut=DutPlug)
def test_firmware(test, dut):
test.measurements.firmware_version = dut.query_firmware()
def main():
test = htf.Test(
test_power,
test_firmware,
procedure_id="FCT-001",
part_number="PCBA-100",
)
with TofuPilot(test):
test.execute(test_start=lambda: input("Scanner le numéro de série : "))
if __name__ == "__main__":
main()Version pytest
import pytest
from tofupilot import TofuPilotClient
@pytest.fixture(scope="session")
def tofupilot():
"""Initialiser le client TofuPilot pour la session de test."""
return TofuPilotClient()
@pytest.fixture(scope="session")
def serial_number():
"""Demander le numéro de série à l'opérateur avant l'exécution des tests."""
return input("Scanner le numéro de série : ")
@pytest.fixture
def dut():
"""Gérer la connexion au DUT. Équivalent d'un Plug OpenHTF."""
# Setup
connection = {"connected": True} # Remplacer par une vraie connexion
yield connection
# Teardown
connection["connected"] = False
def read_voltage() -> float:
"""Lire la tension depuis l'instrument."""
return 3.31 # Remplacer par une vraie lecture
def query_firmware() -> str:
"""Interroger la version firmware du DUT."""
return "2.1.0" # Remplacer par une vraie requête
def test_power_rail(dut):
"""Vérifier que le rail d'alimentation 3,3 V est dans les spécifications."""
voltage = read_voltage()
assert 3.2 <= voltage <= 3.4, f"Rail 3,3 V hors plage : {voltage} V"
def test_firmware_version(dut):
"""Vérifier que la version firmware correspond à celle attendue."""
version = query_firmware()
assert version == "2.1.0", f"Firmware inattendu : {version}"Différences clés dans le code
| Aspect | OpenHTF | pytest |
|---|---|---|
| Mesures | Déclaratives avec @htf.measures. Nom, limites, unités définis à l'avance. | Implicites via assert. Pas de métadonnées structurées. |
| Numéro de série | Intégré dans test.execute(). | Fixture personnalisée ou appel input. |
| Cycle de vie du DUT | Classe Plug avec setUp/tearDown. Injecté via le décorateur @htf.plug(). | Fixture avec yield. Passé manuellement. |
| Limites | .in_range(3.2, 3.4) stocké comme données structurées. | assert 3.2 <= v <= 3.4 est du code, pas des données. |
| Sortie | Protobuf structuré avec toutes les métadonnées. | Réussite/échec uniquement (sauf ajout de rapports personnalisés). |
| Timeouts | @htf.PhaseOptions(timeout_s=10) | @pytest.mark.timeout(10) (nécessite un plugin) |
Quand utiliser OpenHTF
OpenHTF est le meilleur choix lorsque :
- Vous exécutez des tests de production. Le modèle de mesure intégré (nom, valeur, limites, unités) correspond directement à la façon dont les données de test de fabrication doivent être stockées et analysées. Pas besoin de le construire vous-même.
- Vous avez besoin d'interaction avec l'opérateur. OpenHTF dispose d'une interface web intégrée pour que les opérateurs scannent les numéros de série, voient la progression des tests et consultent les résultats. Avec pytest, vous devriez construire cela de zéro.
- Vous voulez des données de test structurées. Chaque mesure dans OpenHTF porte son nom, sa valeur, ses limites, ses unités et son statut réussite/échec. Cela alimente directement TofuPilot pour l'analyse FPY, Cpk et cartes de contrôle. Avec pytest, vous n'obtenez que réussite/échec par fonction de test, rien de plus.
- Votre équipe utilise déjà OpenHTF. Si vous avez des tests OpenHTF existants, restez dessus. Le coût de migration ne vaut pas le changement.
Quand utiliser pytest
pytest est le meilleur choix lorsque :
- Vous faites de la R&D ou des tests de validation. La flexibilité de pytest brille quand les exigences de test changent fréquemment. Pas d'ordonnancement strict des phases, possibilité de sauter ou sélectionner des tests, messages d'assertion riches.
- Vous avez besoin de tester des DUT en parallèle. pytest-xdist exécute les tests en parallèle nativement. L'exécuteur d'OpenHTF est mono-thread.
- Votre équipe connaît pytest. La plupart des développeurs Python connaissent déjà pytest. OpenHTF a une courbe d'apprentissage et une documentation limitée.
- Vous voulez un écosystème de plugins. pytest-timeout, pytest-repeat, pytest-html, pytest-cov. Des milliers de plugins pour tous les besoins. OpenHTF n'en a presque aucun.
- Vous testez du firmware/logiciel sur du matériel. Si le « test » est en réalité un test logiciel qui s'exécute sur du matériel (flashage de firmware, tests d'intégration sur une carte de développement), pytest est le choix naturel.
Utiliser les deux ensemble
Certaines équipes utilisent les deux. pytest pour la validation firmware et la CI, OpenHTF pour les tests fonctionnels de production. Cela fonctionne bien lorsque les exigences de test sont réellement différentes :
| Étape de test | Framework | Pourquoi |
|---|---|---|
| CI firmware | pytest | S'exécute dans le pipeline CI, exécution parallèle, tests de type logiciel |
| Validation EVT/DVT | pytest | Flexible, exploratoire, exigences qui changent fréquemment |
| FCT PVT/Production | OpenHTF | Mesures structurées, interface opérateur, journalisation des données de production |
| Inspection d'entrée | OpenHTF | Répétable, piloté par l'opérateur, nécessite de la traçabilité |
Les deux frameworks fonctionnent avec TofuPilot. OpenHTF a une intégration native (une ligne). pytest fonctionne via le SDK Python (quelques lignes de plus, mêmes données).
Comparaison de l'intégration TofuPilot
OpenHTF : une ligne
from tofupilot.openhtf import TofuPilot
# Encapsuler l'exécution de votre test
with TofuPilot(test):
test.execute(test_start=lambda: input("Numéro de série : "))
# Mesures, limites, unités, phases, pièces jointes
# sont tous enregistrés automatiquement.pytest : SDK Python
from tofupilot import TofuPilotClient
client = TofuPilotClient()
# Après avoir collecté les résultats, créer une exécution
client.create_run(
procedure_id="FCT-001",
unit_under_test={"serial_number": "SN-001", "part_number": "PCBA-100"},
run_passed=True,
steps=[
{
"name": "test_power_rail",
"step_passed": True,
"measurements": [
{
"name": "rail_3v3",
"value": 3.31,
"unit": "V",
"lower_limit": 3.2,
"upper_limit": 3.4,
},
],
},
],
)Plus de code, mais vous obtenez les mêmes analyses dans TofuPilot : FPY, Cpk, cartes de contrôle, Pareto de défaillances, traçabilité.
Matrice de décision
Répondez à ces questions :
| Question | Si oui → | Si non → |
|---|---|---|
| Exécutez-vous des tests de production/fabrication ? | OpenHTF | pytest |
| Les opérateurs interagissent-ils avec le poste de test ? | OpenHTF | pytest |
| Avez-vous besoin de données de mesure structurées (limites, unités) ? | OpenHTF | pytest |
| Avez-vous besoin de tester des DUT en parallèle ? | pytest | L'un ou l'autre |
| Votre équipe découvre les deux frameworks ? | pytest (plus facile à apprendre) | N/A |
| Avez-vous besoin d'un riche écosystème de plugins ? | pytest | L'un ou l'autre |
| Faites-vous des tests CI firmware ? | pytest | N/A |
Si vous avez répondu « oui » aux trois premières questions, commencez par OpenHTF. Pour tout le reste, pytest est le choix le plus sûr. Les deux fonctionnent avec TofuPilot.