Un script de test de production s'exécute des centaines de fois par jour. Il doit être fiable, maintenable et facile à utiliser pour les opérateurs. Ce guide explique comment structurer un script de test OpenHTF prêt pour la ligne de production, pas seulement pour votre banc de développement.
Anatomie d'un script de test
Chaque script de test de production suit la même structure :
imports
classes de plugs (pilotes d'instruments)
fonctions de phase (étapes de test)
assemblage du test (tout connecter)
main (point d'entrée)Respectez cet ordre. C'est ce que chaque ingénieur test de votre équipe s'attend à trouver.
Étape 1 : Définir vos plugs
Les plugs gèrent les connexions aux instruments. Un plug par type d'instrument. setUp() ouvre la connexion. tearDown() la ferme. OpenHTF gère le cycle de vie automatiquement.
import openhtf as htf
from openhtf.plugs import BasePlug
class PowerSupplyPlug(BasePlug):
"""Contrôle de l'alimentation de banc."""
def setUp(self):
self.output_on = False
# Remplacez : rm = pyvisa.ResourceManager("@py")
# self.instr = rm.open_resource("TCPIP::192.168.1.101::INSTR")
def enable(self):
self.output_on = True
# Remplacez : self.instr.write(":OUTP ON")
def disable(self):
self.output_on = False
# Remplacez : self.instr.write(":OUTP OFF")
def tearDown(self):
self.disable()
class DmmPlug(BasePlug):
"""Multimètre numérique pour les mesures de tension et de courant."""
def setUp(self):
self._readings = iter([3.31, 5.01, 0.12])
# Remplacez : rm = pyvisa.ResourceManager("@py")
# self.instr = rm.open_resource("TCPIP::192.168.1.100::INSTR")
def read_voltage(self) -> float:
return next(self._readings)
# Remplacez : return float(self.instr.query(":MEAS:VOLT:DC?"))
def read_current(self) -> float:
return next(self._readings)
# Remplacez : return float(self.instr.query(":MEAS:CURR:DC?"))
def tearDown(self):
pass
# Remplacez : self.instr.close()Règles pour les plugs :
- Un plug par type d'instrument (pas par instance d'instrument)
tearDown()doit toujours laisser l'instrument dans un état sûr (sortie désactivée, connexion fermée)- Ne mettez pas les limites de mesure dans les plugs. Les plugs lisent les valeurs brutes. Les phases appliquent les limites.
Étape 2 : Écrire les fonctions de phase
Chaque phase teste une chose logique. Gardez les phases ciblées et indépendantes.
import openhtf as htf
from openhtf.util import units
@htf.measures(
htf.Measurement("rail_3v3")
.in_range(3.2, 3.4)
.with_units(units.VOLT)
.doc("Rail d'alimentation 3.3V"),
htf.Measurement("rail_5v0")
.in_range(4.8, 5.2)
.with_units(units.VOLT)
.doc("Rail d'alimentation 5.0V"),
)
@htf.plug(psu=PowerSupplyPlug, dmm=DmmPlug)
def test_power_rails(test, psu, dmm):
"""Alimenter le DUT et vérifier les rails de tension."""
psu.enable()
test.measurements.rail_3v3 = dmm.read_voltage()
test.measurements.rail_5v0 = dmm.read_voltage()
@htf.measures(
htf.Measurement("idle_current")
.in_range(0.05, 0.20)
.with_units(units.AMPERE)
.doc("Consommation de courant au repos de la carte"),
)
@htf.plug(dmm=DmmPlug)
def test_current(test, dmm):
"""Mesurer la consommation de courant au repos."""
test.measurements.idle_current = dmm.read_current()Règles pour les phases :
- Un bloc
@htf.measurespar phase. Déclarez tout ce que la phase va mesurer. - Utilisez le décorateur
@htf.plug(), pas les annotations de type, pour l'injection des plugs. - Les noms de phase doivent décrire ce qu'elles testent :
test_power_rails, pasphase_1. - Ajoutez
.doc()à chaque mesure. Cela apparaît dans TofuPilot et les rapports.
Étape 3 : Ordonner correctement les phases
L'ordre des phases est important en production. Testez d'abord l'élément le plus critique. Si les rails d'alimentation échouent, ne perdez pas de temps à tester la communication.
| Ordre | Phase | Pourquoi cet ordre |
|---|---|---|
| 1 | test_power_rails | Si l'alimentation échoue, tout le reste échouera aussi |
| 2 | test_current | Courant élevé = court-circuit = arrêter avant d'endommager |
| 3 | test_communication | Vérifier que le firmware est actif avant les tests fonctionnels |
| 4 | test_functional | Comportement au niveau de la carte |
| 5 | test_calibration | Réglage fin (uniquement si les étapes précédentes réussissent) |
Étape 4 : Assembler le test
import openhtf as htf
from tofupilot.openhtf import TofuPilot
def main():
test = htf.Test(
test_power_rails,
test_current,
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()procedure_id identifie le type de test. part_number identifie le produit. Les deux apparaissent dans TofuPilot pour le filtrage et les analyses.
Étape 5 : Supporter plusieurs SKU
Lorsque la même station de test teste différents produits, utilisez une fonction factory.
import openhtf as htf
from tofupilot.openhtf import TofuPilot
SKU_CONFIG = {
"PCBA-100": {
"procedure_id": "FCT-001",
"phases": [test_power_rails, test_current],
},
"PCBA-200": {
"procedure_id": "FCT-002",
"phases": [test_power_rails, test_current],
},
}
def create_test(part_number: str) -> htf.Test:
"""Créer un test configuré pour un SKU spécifique."""
config = SKU_CONFIG[part_number]
return htf.Test(
*config["phases"],
procedure_id=config["procedure_id"],
part_number=part_number,
)
def main():
part_number = input("Scanner le numéro de pièce : ").strip()
if part_number not in SKU_CONFIG:
print(f"Numéro de pièce inconnu : {part_number}")
return
test = create_test(part_number)
with TofuPilot(test):
test.execute(test_start=lambda: input("Scanner le numéro de série : "))Organisation des fichiers
Pour une station de test mono-produit, un seul fichier suffit. Pour du multi-produit ou des tests complexes, divisez en modules :
fct/
├── __init__.py
├── plugs/
│ ├── __init__.py
│ ├── power_supply.py # PowerSupplyPlug
│ ├── dmm.py # DmmPlug
│ └── uart.py # UartPlug
├── phases/
│ ├── __init__.py
│ ├── power.py # test_power_rails
│ ├── current.py # test_current
│ └── communication.py # test_communication
├── config.py # Configurations SKU, limites
└── main.py # Assemblage du test et point d'entréeQuand diviser : Si votre fichier de test dépasse 300 lignes, divisez-le. Si vous avez plus de 5 plugs, séparez-les dans un répertoire plugs/. Si vous testez plus de 3 SKU, déplacez la configuration dans son propre fichier.
Erreurs courantes
| Erreur | Problème | Solution |
|---|---|---|
| Mettre les limites dans les plugs | Impossible de réutiliser le plug pour différents produits | Gardez les limites dans @htf.measures |
| Utiliser les annotations de type pour l'injection des plugs | dut: DutPlug n'injecte pas | Utilisez le décorateur @htf.plug(dut=DutPlug) |
Ne pas appeler tearDown() dans les plugs | Instruments laissés dans un état inconnu | Implémentez toujours tearDown() |
| Tout tester dans une seule phase | Un échec masque les autres | Divisez en phases ciblées |
| Coder en dur les adresses des instruments | Impossible de déplacer vers une autre station | Utilisez un fichier de configuration ou des variables d'environnement |
| Pas de timeout sur les phases | Un test bloqué immobilise la station | Ajoutez @htf.PhaseOptions(timeout_s=30) |