Pourquoi votre pipeline a besoin de tests et d'analyses avant qu'il ne soit trop tard
Vous venez de terminer la construction de votre application. Le build a réussi. L'artefact existe. Et maintenant ?
De nombreuses équipes s'arrêtent là. Elles partent du principe que si le code compile et que le build passe, l'artefact est prêt pour la production. Mais un build réussi vous dit seulement que le code peut être assemblé. Il ne vous dit pas si le code fonctionne réellement, s'il a des failles de sécurité, ou s'il plantera lorsqu'il communiquera avec la base de données.
Sauter les vérifications à ce stade, c'est comme expédier un colis sans regarder à l'intérieur. Vous pourriez envoyer quelque chose de cassé, de dangereux, ou les deux.
Commencez par le retour le plus rapide : les tests unitaires
La première vérification dans tout pipeline devrait être les tests unitaires. Ces tests vérifient le comportement de votre code de l'intérieur. Vous appelez une fonction, un cas d'utilisation ou un point d'accès, et vous vérifiez si le résultat correspond à ce que vous attendez.
Le diagramme suivant montre l'ordre des vérifications et les points de décision critiques où un échec arrête le pipeline :
Les tests unitaires exécutent vos couches logiques réelles, du point d'entrée jusqu'au module le plus profond. Ils n'ont pas besoin d'une vraie base de données, de services externes ou d'appels réseau. C'est ce qui les rend rapides. Une bonne suite de tests unitaires s'exécute en quelques secondes ou quelques minutes au maximum.
Voici un extrait de pipeline YAML minimal qui exécute des tests unitaires et un scan de vulnérabilités, en arrêtant le pipeline si l'un des deux échoue :
jobs:
test-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
- name: Run vulnerability scan
run: npm audit --audit-level=high
Si un test unitaire échoue, arrêtez immédiatement le pipeline. Il est inutile de continuer si le comportement de base de votre code est cassé. Tout le reste repose sur l'hypothèse que le code fait ce qu'il est censé faire. Si cette hypothèse est fausse, chaque vérification suivante est un effort gaspillé.
Vérifiez si les pièces s'emboîtent réellement : les tests d'intégration
Les tests unitaires prouvent qu'un comportement significatif fonctionne lorsque le monde extérieur est contrôlé. Vos couches internes peuvent toujours fonctionner ensemble, mais les systèmes voisins sont simulés ou maintenus sous contrôle de test. Le logiciel se brise également lorsqu'il doit communiquer avec des dépendances réelles. C'est là que les tests d'intégration entrent en jeu.
Les tests d'intégration vérifient si vos modules peuvent coopérer correctement. Le module utilisateur peut-il enregistrer des données dans la base de données ? L'API répond-elle correctement lorsque le service de paiement est indisponible ? Les formats de données correspondent-ils entre les services ?
Ces tests sont plus lents que les tests unitaires car ils nécessitent une infrastructure réelle : une base de données, une file d'attente de messages ou une instance de test d'un autre service. Mais c'est précisément là que se cachent la plupart des bugs du monde réel. Un code qui réussit tous les tests unitaires peut encore échouer aux tests d'intégration à cause de :
- Chaînes de connexion à la base de données incorrectes
- Schémas de données incompatibles
- Valeurs de configuration incorrectes
- Variables d'environnement manquantes
Les tests d'intégration détectent le genre de problèmes qui n'apparaissent que lorsque les composants interagissent réellement. Si vous les sautez, vous pariez que votre code fonctionnera parfaitement dans un environnement que vous n'avez pas testé.
Analysez le code lui-même : l'analyse statique
Les tests fonctionnels vérifient ce que fait le code. L'analyse statique vérifie comment le code est écrit. Elle lit votre code source sans l'exécuter et recherche des motifs problématiques.
Les outils d'analyse statique peuvent détecter :
- Des variables déclarées mais jamais utilisées
- Du code trop complexe ou trop profondément imbriqué
- Des déréférencements potentiels de pointeurs nuls
- Des violations des normes de codage convenues par votre équipe
- Des motifs sensibles pour la sécurité, comme des identifiants codés en dur
L'analyse statique ne détectera pas les bugs de logique. Mais elle détecte le genre d'erreurs que les développeurs commettent des dizaines de fois par jour et manquent souvent lors de la revue de code. Elle impose également une cohérence au sein de l'équipe. Lorsque chaque commit est vérifié par rapport aux mêmes règles, la base de code reste maintenable même lorsque l'équipe grandit.
Trouvez les dangers cachés : le scan de vulnérabilités
La plupart des vulnérabilités de sécurité dans les applications modernes ne proviennent pas du code que vous avez écrit. Elles proviennent des bibliothèques et des paquets dont vous dépendez. Une seule dépendance obsolète avec une faille connue peut compromettre l'ensemble de votre application.
Le scan de vulnérabilités vérifie votre liste de dépendances par rapport aux bases de données de problèmes de sécurité connus. Si une bibliothèque présente une vulnérabilité critique, le scanner la signale et le pipeline doit s'arrêter. Mieux vaut retarder une mise en production que de livrer une faille de sécurité connue en production.
Ce scan doit être exécuté à chaque build, pas seulement avant les versions majeures. De nouvelles vulnérabilités sont découvertes chaque jour. Une bibliothèque qui était sûre la semaine dernière pourrait avoir un CVE critique publié aujourd'hui. Une analyse régulière garantit que vous détectez ces problèmes avant qu'ils n'atteignent les utilisateurs.
Conservez les preuves : pourquoi les résultats doivent être sauvegardés
Chaque vérification à ce stade produit des résultats. Les tests unitaires réussissent ou échouent. Les tests d'intégration rapportent quels scénarios ont fonctionné. L'analyse statique liste les avertissements et les erreurs. Les scans de vulnérabilités signalent les dépendances.
Ces résultats sont des preuves. Ils prouvent que le pipeline a effectué ses vérifications et montrent ce qui s'est passé. Sauvegardez-les.
Les preuves sont importantes pour trois raisons :
Débogage des problèmes de production. Lorsque quelque chose ne va pas en production, vous pouvez vérifier si le pipeline a détecté le problème. Si c'est le cas, vous savez que la vérification a fonctionné. Si ce n'est pas le cas, vous savez que vous avez besoin d'un meilleur test.
Audit et conformité. Les régulateurs, les clients et les politiques internes exigent souvent la preuve que chaque changement a été testé avant sa mise en production. Les preuves sauvegardées satisfont à cette exigence.
Analyse des tendances. Au fil du temps, les preuves montrent si la qualité de votre code s'améliore. Les tests échouent-ils moins souvent ? Les vulnérabilités diminuent-elles ? Certains modules sont-ils systématiquement problématiques ? Ces données vous aident à décider où investir vos efforts d'amélioration.
Stockez les preuves dans un format lisible par machine, comme JUnit XML ou SARIF, et conservez également un résumé lisible par un humain. Placez-les dans un endroit qui persiste après la fin du pipeline, comme un registre d'artefacts ou un bucket de stockage dédié. Ne vous fiez pas aux logs du pipeline qui sont nettoyés après quelques jours.
Liste de contrôle pratique pour cette étape
Avant de déplacer un artefact vers le déploiement, confirmez que ces vérifications sont en place :
- Les tests unitaires s'exécutent à chaque commit et font échouer le pipeline s'ils sont cassés
- Les tests d'intégration couvrent les interactions critiques entre composants
- L'analyse statique applique les normes de qualité du code
- Le scan de vulnérabilités vérifie toutes les dépendances
- Tous les résultats sont sauvegardés comme preuves avec des horodatages et des identifiants de commit
Quelle est la suite
Une fois que les tests et les analyses sont passés, vous savez que l'artefact mérite d'être conservé. Il fonctionne correctement, le code est maintenable et les dépendances sont sûres. Vous devez maintenant l'empaqueter et le stocker pour pouvoir le déployer plus tard.
Mais si une vérification échoue, le pipeline s'arrête. L'équipe est notifiée. L'artefact n'atteint jamais l'étape suivante. C'est le but : détecter le problème ici, pas en production.
Le coût de la découverte d'un bug en production est exponentiellement plus élevé que de le trouver dans le pipeline. Un test échoué coûte quelques minutes de temps de développement. Une panne de production coûte la confiance des utilisateurs, des interventions d'incident et des sessions de débogage tardives.
Testez et analysez tôt. Testez et analysez automatiquement. Et conservez les preuves. Votre futur vous remerciera quand quelque chose tournera mal et que vous pourrez prouver exactement ce qui a été vérifié et quand.