Code source de services.api_ihm.tests.run_tests

"""
Script d'exécution des tests avec génération de rapports XML pour Jenkins.
Ce script permet d'exécuter automatiquement tous les tests unitaires du projet et de générer
un rapport au format XML compatible avec Jenkins pour l'intégration continue.
"""

import unittest
import sys
import os
import xml.etree.ElementTree as ET
from datetime import datetime
import logging

# Configuration du système de logging pour tracer l'exécution des tests
# Les logs sont affichés dans la console et sauvegardés dans un fichier
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),  # Affichage console
        logging.FileHandler(  # Sauvegarde dans un fichier
            os.path.join(os.path.dirname(__file__), 'reports',
                         'test_execution.log'))
    ])
logger = logging.getLogger(__name__)

# Ajout du répertoire source au PYTHONPATH pour permettre l'import des modules à tester
src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))
sys.path.insert(0, src_path)
logger.info(f"Ajout du chemin source au PYTHONPATH: {src_path}")


[docs] class XMLTestResult(unittest.TestResult): """Classe personnalisée pour générer des résultats de test au format XML. Cette classe étend unittest.TestResult pour capturer les résultats des tests et les formater en XML compatible avec Jenkins. Elle trace également le temps d'exécution de chaque test et leur statut (succès, échec, erreur). """
[docs] def __init__(self, *args, **kwargs): """Initialise le collecteur de résultats de tests.""" super().__init__(*args, **kwargs) self.test_cases = [] # Liste pour stocker les résultats de chaque test logger.info("Initialisation du XMLTestResult")
[docs] def startTest(self, test): """Appelé au début de chaque test pour enregistrer son temps de démarrage.""" self.start_time = datetime.now() logger.info(f"Démarrage du test: {test.id()}") super().startTest(test)
[docs] def addSuccess(self, test): """Enregistre un test réussi avec sa durée d'exécution.""" duration = (datetime.now() - self.start_time).total_seconds() logger.info(f"Test réussi: {test.id()} (durée: {duration:.3f}s)") self.test_cases.append({ 'name': test.id(), 'status': 'success', 'time': duration }) super().addSuccess(test)
[docs] def addError(self, test, err): """Enregistre une erreur survenue pendant l'exécution d'un test.""" duration = (datetime.now() - self.start_time).total_seconds() logger.error(f"Erreur dans le test {test.id()}: {str(err[1])}") logger.error(f"Traceback: {str(err[2])}") self.test_cases.append({ 'name': test.id(), 'status': 'error', 'message': str(err[1]), 'time': duration }) super().addError(test, err)
[docs] def addFailure(self, test, err): """Enregistre un échec de test (assertion non validée).""" duration = (datetime.now() - self.start_time).total_seconds() logger.warning(f"Échec du test {test.id()}: {str(err[1])}") self.test_cases.append({ 'name': test.id(), 'status': 'failure', 'message': str(err[1]), 'time': duration }) super().addFailure(test, err)
[docs] def generate_xml(self): """Génère le rapport XML au format attendu par Jenkins. Crée une structure XML contenant tous les résultats des tests avec leurs statuts, durées et messages d'erreur éventuels. """ logger.info("Génération du rapport XML") root = ET.Element('testsuites') testsuite = ET.SubElement(root, 'testsuite', name='api_ihm_tests', tests=str(self.testsRun), errors=str(len(self.errors)), failures=str(len(self.failures))) # Ajout de chaque cas de test au rapport for case in self.test_cases: testcase = ET.SubElement(testsuite, 'testcase', name=case['name'], time=str(case['time'])) if case['status'] in ['error', 'failure']: failure = ET.SubElement(testcase, case['status']) failure.text = case.get('message', '') logger.info( f"Rapport XML généré avec {self.testsRun} tests, " f"{len(self.errors)} erreurs et {len(self.failures)} échecs") return ET.ElementTree(root)
[docs] def run_tests(): """Exécute la suite de tests et génère le rapport. Cette fonction : 1. Crée le répertoire des rapports si nécessaire 2. Découvre automatiquement tous les tests du projet 3. Exécute les tests et collecte les résultats 4. Génère et sauvegarde le rapport XML 5. Affiche un résumé des résultats Returns: int: 0 si tous les tests sont réussis, 1 sinon """ try: # Création du répertoire pour les rapports de test reports_dir = os.path.join(os.path.dirname(__file__), 'reports') os.makedirs(reports_dir, exist_ok=True) logger.info(f"Répertoire des rapports créé: {reports_dir}") # Découverte automatique des tests (fichiers commençant par 'test_') loader = unittest.TestLoader() logger.info("Recherche des tests...") suite = loader.discover(start_dir=os.path.dirname(__file__), pattern='test_*.py') logger.info(f"Tests trouvés: {suite.countTestCases()} cas de test") # Exécution des tests avec notre collecteur de résultats personnalisé logger.info("Démarrage de l'exécution des tests") result = XMLTestResult() suite.run(result) # Génération et sauvegarde du rapport au format XML xml_report = result.generate_xml() report_path = os.path.join(reports_dir, 'test-results.xml') xml_report.write(report_path, encoding='utf-8', xml_declaration=True) logger.info(f"Rapport XML sauvegardé: {report_path}") # Affichage du résumé des résultats success = result.wasSuccessful() logger.info("=== Résumé des tests ===") logger.info(f"Tests exécutés: {result.testsRun}") logger.info( f"Succès: {result.testsRun - len(result.errors) - len(result.failures)}" ) logger.info(f"Erreurs: {len(result.errors)}") logger.info(f"Échecs: {len(result.failures)}") logger.info(f"Statut global: {'Succès' if success else 'Échec'}") return 0 if success else 1 except Exception as e: logger.error(f"Erreur lors de l'exécution des tests: {str(e)}", exc_info=True) return 1
if __name__ == '__main__': try: logger.info("=== Démarrage de la suite de tests ===") exit_code = run_tests() logger.info( f"=== Fin de la suite de tests (code de sortie: {exit_code}) ===") sys.exit(exit_code) except KeyboardInterrupt: # Gestion de l'interruption manuelle (Ctrl+C) logger.warning("Interruption manuelle des tests") sys.exit(130) except Exception as e: # Gestion des erreurs inattendues logger.error(f"Erreur fatale: {str(e)}", exc_info=True) sys.exit(1)