vendredi 25 septembre 2026

À propos

Contact

Tests

Tests de caractérisation : sécuriser un code WordPress hérité avant d’y toucher

Comment capturer le comportement réel d'un code WordPress sans le moindre test existant, pour pouvoir ensuite le modifier sans avancer à l'aveugle.

Par Clément Hadrot • 17 octobre 2024 • 6 min de lecture • Aucun commentaire
Tests de caractérisation : sécuriser un code WordPress hérité avant d'y toucher

« On ne touche plus à ce fichier, personne ne sait exactement ce qu’il fait. » Cette phrase, prononcée lors d’un audit chez un client qui gérait une extension de tarification complexe pour un site B2B, résume bien le problème : une fonction de 400 lignes, aucun test, des règles de calcul accumulées au fil de six années de demandes clients successives, et une peur collective de la modifier de peur de casser une règle métier invisible. Ajouter un test qui vérifie « ce que la fonction devrait faire » est impossible : personne ne sait plus vraiment ce qu’elle devrait faire, seulement ce qu’elle fait.

C’est exactement la situation où les tests de caractérisation trouvent leur utilité. Contrairement à un test unitaire classique, qui vérifie qu’un comportement attendu est respecté, un test de caractérisation se contente de figer le comportement observé, bug éventuel compris, pour donner un filet de sécurité avant toute intervention.

La différence essentielle avec un test unitaire classique

Un test unitaire traditionnel part d’une spécification : on sait ce que la fonction doit faire, on écrit le test avant ou en même temps que le code, et un échec signale une régression par rapport à cette spécification. Un test de caractérisation part de l’autre bout : on ignore ou on n’est plus certain de la spécification, on observe ce que le code fait réellement aujourd’hui pour un jeu d’entrées donné, et on fige ce résultat comme référence.

Cela signifie assumer, temporairement, de figer aussi des comportements qui sont en réalité des bugs. Ce n’est pas gênant : l’objectif immédiat n’est pas de corriger le code, mais de pouvoir le modifier ensuite sans provoquer de régression non détectée. Une fois le filet de sécurité en place, corriger un bug identifié devient un choix conscient, avec un test mis à jour en conséquence, plutôt qu’un accident de refactorisation.

Étape 1 : cartographier les entrées et sorties observables

Avant d’écrire le moindre test, on liste les points d’entrée réels de la fonction ciblée : quels paramètres, quelles options globales, quel état de base de données influencent son résultat. Sur la fonction de tarification citée en introduction, cette cartographie a révélé qu’elle lisait silencieusement trois options WordPress différentes en plus de ses paramètres explicites, un couplage caché qu’aucun développeur n’avait mentionné spontanément.

L'essentiel à retenir : On capture le comportement existant, pas le comportement souhaité ; Les tests de caractérisation acceptent les bugs connus, temporairement ; Ils forment un filet de sécurité, pas une garantie de qualité du code

Étape 2 : générer des cas représentatifs, pas exhaustifs

On ne cherche pas à couvrir toutes les combinaisons possibles, ce qui serait souvent combinatoirement impossible, mais à couvrir les cas réellement rencontrés en production : les valeurs limites connues, les configurations client les plus courantes, et quelques cas extrêmes identifiés dans les tickets de support archivés. Pour la fonction de tarification, l’équipe a extrait de la base de production une trentaine de commandes réelles anonymisées, représentatives des combinaisons de remises, taxes et frais de port effectivement rencontrées.

public function test_caracterisation_tarif_cas_reel_1() {
    $commande = $this->charger_fixture( 'commande-anonymisee-01.json' );
    $resultat = calculer_tarif_final( $commande );

    // Valeur figée telle qu'observée, pas déduite d'une règle métier documentée
    $this->assertSame( 84.32, $resultat['total_ttc'] );
    $this->assertSame( 3, $resultat['nombre_lignes_remise'] );
}

Le commentaire dans le test est volontaire : il rappelle que la valeur attendue n’a pas été déduite d’une spécification, mais relevée sur le comportement observé, une nuance importante pour quiconque relira ce test plus tard sans le contexte de sa création.

Étape 3 : automatiser la génération des assertions

Écrire à la main la valeur attendue pour trente cas serait long et source d’erreurs de recopie. Une technique éprouvée consiste à exécuter d’abord le test avec une assertion volontairement fausse ou absente, à laisser PHPUnit afficher la valeur réellement produite dans son message d’échec, puis à copier cette valeur comme référence figée. Certains outils de test de caractérisation automatisent complètement cette étape via des snapshots, qui stockent la sortie sérialisée dans un fichier séparé plutôt que dans une assertion en dur :

public function test_caracterisation_tarif_snapshot() {
    $commande = $this->charger_fixture( 'commande-anonymisee-02.json' );
    $resultat = calculer_tarif_final( $commande );

    $this->assertMatchesJsonSnapshot( wp_json_encode( $resultat ) );
}

Étape 4 : refactoriser à l’abri du filet

Une fois la suite de tests de caractérisation en place et verte, le code peut être découpé, renommé, simplifié, sans que la fonction change de comportement observable pour les cas couverts. Toute modification qui casse un test de caractérisation doit être examinée précisément : est-ce une régression accidentelle, ou une correction volontaire d’un bug désormais identifié ? Dans ce second cas, on met à jour le test en connaissance de cause, jamais en le contournant silencieusement.

Un test de caractérisation qui capture un bug n’est pas un aveu d’échec : c’est la première fois que ce bug devient visible et documenté, plutôt que caché dans du code que personne n’ose lire en entier.

Les limites à ne pas perdre de vue

Ces tests protègent contre les régressions de comportement, ils ne garantissent en rien la qualité de conception du code sous-jacent, ni sa performance, ni sa sécurité. Ils ne remplacent pas non plus un audit fonctionnel avec les parties prenantes métier, seules capables de dire, au final, quels comportements observés sont réellement des règles voulues et lesquels sont des anomalies à corriger.

En résumé

Face à un code hérité sans specification claire ni test existant, tenter d’écrire directement des tests unitaires « corrects » revient souvent à deviner une intention perdue. Les tests de caractérisation offrent une voie plus honnête : capturer d’abord ce qui existe réellement, aussi imparfait soit-il, pour ensuite modifier le code en toute confiance. C’est un préalable, pas une fin en soi : le vrai travail de nettoyage commence une fois ce filet de sécurité posé.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi