Votre code de test ne devrait pas changer entre les environnements. Le même script qui tourne sur votre banc de développement doit fonctionner en validation QA et sur la ligne de production. Ce qui change, c'est la configuration : adresses des instruments, limites de mesure, workspace TofuPilot, niveaux de journalisation.
Ce guide présente trois approches pour la configuration spécifique à chaque environnement et comment pointer chaque environnement vers son propre workspace TofuPilot.
Le problème
Un script de test unique a généralement besoin de valeurs différentes pour :
- Adresses des instruments. Votre multimètre de banc dev est à
TCPIP::192.168.1.10mais la production utiliseGPIB0::22. - Limites de mesure. Le dev utilise des tolérances plus larges pour le débogage. La production utilise les limites de la datasheet.
- Workspace TofuPilot. Les exécutions de dev ne doivent pas polluer les données de production.
- Verbosité de la journalisation. Journalisation de débogage en dev, erreurs uniquement en production.
- Identifiants de fixture. Chaque station a sa propre fixture avec des données de calibration uniques.
Coder en dur l'un de ces éléments signifie modifier le code à chaque déploiement. C'est comme ça que les bugs arrivent sur la ligne de production.
Approche 1 : Variables d'environnement avec python-dotenv
L'approche la plus simple. Créez un fichier .env par environnement et chargez-le au démarrage.
TOFUPILOT_API_KEY=tp_dev_abc123
DMM_ADDRESS=TCPIP::192.168.1.10::INSTR
PSU_ADDRESS=TCPIP::192.168.1.11::INSTR
VOLTAGE_MIN=3.0
VOLTAGE_MAX=3.6
LOG_LEVEL=DEBUG
STATION_ID=DEV-BENCH-01TOFUPILOT_API_KEY=tp_prod_xyz789
DMM_ADDRESS=GPIB0::22::INSTR
PSU_ADDRESS=GPIB0::5::INSTR
VOLTAGE_MIN=3.2
VOLTAGE_MAX=3.4
LOG_LEVEL=ERROR
STATION_ID=PROD-LINE1-STN04Chargez le bon fichier en fonction d'un flag d'environnement :
import os
from dotenv import load_dotenv
# Définir TEST_ENV=dev|qa|production avant l'exécution
env = os.getenv("TEST_ENV", "dev")
load_dotenv(f".env.{env}")
class Config:
TOFUPILOT_API_KEY = os.getenv("TOFUPILOT_API_KEY")
DMM_ADDRESS = os.getenv("DMM_ADDRESS")
PSU_ADDRESS = os.getenv("PSU_ADDRESS")
VOLTAGE_MIN = float(os.getenv("VOLTAGE_MIN", "3.2"))
VOLTAGE_MAX = float(os.getenv("VOLTAGE_MAX", "3.4"))
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
STATION_ID = os.getenv("STATION_ID", "UNKNOWN")Utilisez-le dans votre test :
import openhtf as htf
from tofupilot.openhtf import TofuPilot
from config import Config
@htf.measures(
htf.Measurement("voltage_3v3").in_range(Config.VOLTAGE_MIN, Config.VOLTAGE_MAX),
)
def test_power_rail(test):
# Utiliser Config.DMM_ADDRESS pour se connecter au bon instrument
test.measurements.voltage_3v3 = 3.29
def main():
test = htf.Test(test_power_rail)
with TofuPilot(test):
test.execute(test_start=lambda: "DUT-001")
if __name__ == "__main__":
main()Exécuter avec : TEST_ENV=production python test_power.py
Approche 2 : Fichiers de configuration YAML
Pour des configurations plus complexes avec des structures imbriquées, YAML est plus lisible que des variables d'environnement plates.
instruments:
dmm:
address: "GPIB0::22::INSTR"
timeout_ms: 5000
psu:
address: "GPIB0::5::INSTR"
timeout_ms: 3000
limits:
voltage_3v3:
min: 3.2
max: 3.4
current_idle:
max: 0.050
station:
id: "PROD-LINE1-STN04"
log_level: "ERROR"instruments:
dmm:
address: "TCPIP::192.168.1.10::INSTR"
timeout_ms: 10000
psu:
address: "TCPIP::192.168.1.11::INSTR"
timeout_ms: 10000
limits:
voltage_3v3:
min: 3.0
max: 3.6
current_idle:
max: 0.100
station:
id: "DEV-BENCH-01"
log_level: "DEBUG"Chargez avec un helper simple :
import os
import yaml
from pathlib import Path
def load_config(env: str = None) -> dict:
"""Charger la configuration YAML pour l'environnement donné."""
env = env or os.getenv("TEST_ENV", "dev")
config_path = Path(__file__).parent / "config" / f"{env}.yaml"
if not config_path.exists():
raise FileNotFoundError(f"Pas de fichier de configuration pour l'environnement : {env}")
with open(config_path) as f:
return yaml.safe_load(f)import openhtf as htf
from tofupilot.openhtf import TofuPilot
from config_loader import load_config
cfg = load_config()
limits = cfg["limits"]["voltage_3v3"]
@htf.measures(
htf.Measurement("voltage_3v3").in_range(limits["min"], limits["max"]),
)
def test_power_rail(test):
test.measurements.voltage_3v3 = 3.29
def main():
test = htf.Test(test_power_rail)
with TofuPilot(test):
test.execute(test_start=lambda: "DUT-001")
if __name__ == "__main__":
main()Pointer vers différents workspaces TofuPilot
Chaque environnement devrait écrire dans son propre workspace TofuPilot. Cela garde le bruit du dev hors des données de production.
Définissez la variable d'environnement TOFUPILOT_API_KEY par environnement. Chaque clé API est liée à un workspace. Aucune modification de code nécessaire.
# Dev
export TOFUPILOT_API_KEY="tp_dev_abc123"
python test_power.py
# QA
export TOFUPILOT_API_KEY="tp_qa_def456"
python test_power.py
# Production
export TOFUPILOT_API_KEY="tp_prod_xyz789"
python test_power.pyLe client TofuPilot lit automatiquement TOFUPILOT_API_KEY depuis l'environnement. Vous n'avez pas besoin de le passer dans le code.
Comparaison : Variables d'env vs Fichiers de config vs Arguments CLI
| Approche | Idéal pour | Avantages | Inconvénients |
|---|---|---|---|
| Variables d'environnement | Configs simples, CI/CD, secrets | Pas de fichiers à déployer, fonctionne avec Docker/systemd, adapté aux secrets | Structure plate uniquement, pas d'imbrication |
| Fichiers de configuration YAML | Configs complexes, structures imbriquées | Lisible, supporte les commentaires, versionnable | Fichiers à déployer, secrets en clair |
| Arguments CLI | Surcharges ponctuelles, débogage | Pas de fichiers nécessaires, facile à tester | Verbeux pour de nombreux paramètres, pas persistant |
| Combiné (env + YAML) | Déploiements en production | Secrets dans les env, structure dans YAML | Deux systèmes à maintenir |
L'approche pragmatique : utilisez YAML pour les adresses d'instruments et les limites (les éléments qui varient par station), et les variables d'environnement pour les secrets (clés API, identifiants). Les arguments CLI pour les surcharges rapides pendant le débogage.