Le WordPress d'aujourd'hui, décodé pour les développeurs

Tests

Ne rejouer que les tests touchés par un commit plutôt que toute la suite

Relancer toute une suite déjà longue à chaque commit gaspille du temps de calcul. Le principe de l'analyse d'impact pour ne sélectionner que les tests concernés.

Par WordPress Développement • 19 août 2021 • 4 min de lecture • Aucun commentaire
Ne rejouer que les tests touchés par un commit plutôt que toute la suite

Neuf commits sur dix ne touchent qu’une poignée de fichiers, et pourtant la suite complète tourne à chaque fois dans son intégralité, sans distinction. C’est ce constat qui a donné naissance à l’analyse d’impact (« test impact analysis »), une approche qui ne rejoue que les tests réellement concernés par un changement donné, plutôt que l’intégralité d’une suite déjà longue.

Ce billet présente le principe général de cette sélection ciblée, sans entrer dans la parallélisation des tests ni dans le cache de dépendances, qui répondent à d’autres problèmes de performance.

Le principe : un graphe de dépendances entre code et tests

L’analyse d’impact repose sur une idée simple : construire une carte qui relie chaque test aux fichiers source dont son exécution dépend réellement. Quand un commit modifie un fichier donné, seuls les tests dont le graphe de dépendances touche ce fichier sont sélectionnés pour l’exécution.

Arborescence simplifiée de dépendances

src/
├── Fidelite/
│   ├── Calculateur.php ────────┐
│   └── Notifieur.php           │
├── Commande/                   │
│   └── Panier.php              │
tests/
├── Fidelite/
│   ├── CalculateurTest.php ◄───┘   (dépend de Calculateur.php)
│   └── NotifieurTest.php ◄─────    (dépend de Notifieur.php)
└── Commande/
    └── PanierTest.php ◄──────      (dépend de Panier.php, sans lien avec Fidelite/)

Un commit qui modifie uniquement Calculateur.php ne sélectionne alors que CalculateurTest.php, laissant de côté NotifieurTest.php et PanierTest.php, qui n’ont aucune dépendance vers le fichier modifié.

Deux façons de construire ce graphe

Analyse statique des imports

La méthode la plus simple consiste à parcourir les instructions use ou les appels de classe dans chaque fichier de test, pour en déduire les fichiers source dont il dépend directement. Cette approche est rapide à mettre en place mais rate les dépendances indirectes, par exemple un hook WordPress déclenché par un fichier tiers non importé explicitement.

Instrumentation à l’exécution

Une méthode plus précise consiste à enregistrer, pendant une exécution complète de la suite, quelles lignes de code source sont réellement traversées par chaque test, un peu à la manière d’un outil de couverture de code. Cette carte d’exécution réelle capture aussi les dépendances indirectes, mais demande une exécution initiale complète pour être construite, puis une mise à jour régulière.

L'essentiel à retenir : Un graphe de dépendances relie chaque fichier de test aux fichiers source qu'il couvre ; Seuls les tests dont une dépendance a changé sont rejoués sur un commit donné ; La suite complète doit malgré tout tourner régulièrement pour éviter un angle mort

Sélectionner les tests à partir du diff Git

En pratique, l’outil qui orchestre l’analyse d’impact compare la liste des fichiers modifiés par un commit (via git diff --name-only) à la carte de dépendances construite au préalable, puis dresse la liste des tests à exécuter :

git diff --name-only HEAD~1 HEAD
# src/Fidelite/Calculateur.php

# Résolution via la carte de dépendances :
# → tests/Fidelite/CalculateurTest.php sélectionné
# → tests/Commande/PanierTest.php ignoré (aucune dépendance)

La limite centrale : l’angle mort de la carte incomplète

Toute carte de dépendances, statique ou instrumentée, comporte un risque de désynchronisation avec la réalité du code : un nouveau chemin d’exécution ajouté sans que la carte ne soit régénérée peut faire passer un test à côté d’un changement qui l’affecte réellement. C’est la contrepartie de la vitesse gagnée.

  • La sélection ciblée ne remplace jamais totalement une exécution complète, elle l’accélère au quotidien.
  • Une exécution complète régulière (chaque nuit, ou avant chaque fusion sur la branche principale) reste nécessaire pour rattraper les angles morts de la carte.
  • Un changement de configuration globale (fichier de bootstrap, dépendance Composer) doit forcer une exécution complète, quelle que soit la sélection habituelle.

Une sélection ciblée qui n’est jamais recroisée avec une exécution complète finit par masquer, plutôt que résoudre, les régressions les plus difficiles à repérer.

Quand cette approche devient rentable

En dessous de quelques minutes de suite complète, l’effort de mise en place d’une analyse d’impact dépasse souvent le gain obtenu. L’approche devient pertinente à partir du moment où l’attente d’une suite complète ralentit visiblement le rythme de commit d’une équipe, typiquement au-delà d’une dizaine de minutes par exécution.

En résumé

L’analyse d’impact ne rejoue que les tests dont la dépendance vers le code modifié est avérée, grâce à un graphe construit soit par analyse statique des imports, soit par instrumentation de l’exécution réelle. Ce gain de vitesse quotidien doit toujours rester complété par des exécutions complètes régulières, seules capables de rattraper les angles morts inévitables d’une carte de dépendances jamais parfaitement à jour.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi