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

Tests

Pourquoi vos tests WordPress tournent déjà dans une transaction annulée à la fin

Un article inséré en test disparaît sans qu'aucune ligne ne le supprime explicitement. Le mécanisme de rollback automatique expliqué, et ses limites réelles.

Par WordPress Développement • 25 juin 2020 • 5 min de lecture • Aucun commentaire
Pourquoi vos tests WordPress tournent déjà dans une transaction annulée à la fin

Pourquoi un article créé au début d’un test n’existe-t-il plus dans le test suivant, alors qu’aucun appel à wp_delete_post() n’apparaît nulle part dans le code ? Cette question revient régulièrement chez les développeurs qui découvrent WP_UnitTestCase après avoir écrit des tests PHP classiques.

La réponse tient en un mécanisme simple mais peu documenté dans le détail : chaque test s’exécute à l’intérieur d’une transaction SQL ouverte avant lui et annulée après lui. Ce billet explique ce mécanisme et ses limites face au DDL ou aux transactions manuelles imbriquées ; il ne traite ni les factories ni les fixtures, qui sont un sujet distinct.

Symptôme : une donnée insérée disparaît sans suppression explicite

Le scénario typique ressemble à ceci : un premier test insère un article via $this->factory->post->create(), vérifie son existence, et tout se passe bien. Un second test, censé partir d’une base vide, s’exécute ensuite sans jamais retrouver cet article, alors même qu’aucune ligne de nettoyage n’a été écrite entre les deux :

class Test_Articles extends WP_UnitTestCase {

    public function test_creation_article() {
        $id = $this->factory->post->create();
        $this->assertIsInt( $id );
    }

    public function test_base_vide_au_depart() {
        $requete = new WP_Query( [ 'post_type' => 'post' ] );
        $this->assertSame( 0, $requete->found_posts );
    }
}

Ce second test passe systématiquement, quel que soit l’ordre d’exécution, ce qui surprend le développeur habitué à devoir nettoyer sa base entre chaque scénario.

Diagnostic : un rollback ouvert dans setUp, fermé dans tearDown

La classe WP_UnitTestCase hérite d’un comportement fourni par le framework de tests du cœur WordPress : à chaque setUp(), une transaction SQL démarre ; à chaque tearDown(), cette transaction est annulée via un ROLLBACK. Toute insertion effectuée pendant le test, qu’il s’agisse d’un article, d’une option ou d’un utilisateur, est donc annulée automatiquement, sans qu’aucun code de nettoyage n’ait besoin d’être écrit par le développeur.

L'essentiel à retenir : Chaque test tourne dans une transaction ouverte puis annulée ; Le rollback est automatique, aucun nettoyage manuel n'est nécessaire ; Le DDL et les transactions manuelles imbriquées échappent à ce mécanisme

Ce mécanisme explique aussi pourquoi la base de test peut rester relativement stable en volume au fil de centaines d’exécutions : rien ne s’accumule réellement, puisque chaque transaction repart de zéro.

Correctif : ce n’est pas un bug, mais il faut connaître deux limites

Il n’y a rien à corriger dans le cas standard : le mécanisme fonctionne comme prévu. Deux situations, en revanche, échappent à ce filet de sécurité et méritent une vigilance particulière.

Le DDL ne peut pas être annulé

Une instruction de modification de structure (CREATE TABLE, ALTER TABLE) provoque, sur la plupart des moteurs MySQL, un commit implicite qui referme la transaction en cours. Un test qui crée une table personnalisée pour la tester laisse donc cette table en place après le test, et casse potentiellement l’isolation des tests suivants :

public function test_creation_table_personnalisee() {
    global $wpdb;
    $wpdb->query( "CREATE TABLE {$wpdb->prefix}stats_visites ( id INT )" );
    // Cette instruction referme la transaction ouverte par WP_UnitTestCase.
}

Pour ce cas précis, il faut supprimer explicitement la table dans un tearDown() personnalisé, sans compter sur le rollback automatique.

Les transactions manuelles imbriquées cassent le mécanisme

Un code de production qui ouvre lui-même une transaction ($wpdb->query( 'START TRANSACTION' )) puis effectue un COMMIT valide définitivement les données insérées à l’intérieur, indépendamment de la transaction ouverte par le test. Le rollback de fin de test ne peut alors annuler que ce qui reste dans sa propre portée transactionnelle.

Prévention : repérer les opérations qui échappent au rollback

  • Éviter toute instruction DDL dans un test ; si elle est indispensable, prévoir un nettoyage manuel explicite.
  • Se méfier d’un code de production qui gère ses propres transactions et le tester avec un tearDown() qui vérifie l’état final de la table concernée.
  • En cas de doute sur une pollution de base entre tests, lancer la suite avec --filter sur un seul test suspect et comparer l’état de la base avant et après.

Un test qui laisse une trace après son exécution mérite d’être isolé en premier lors d’un débogage de suite instable : il est souvent le seul à ne pas respecter le rollback automatique.

En résumé

Le rollback automatique de WP_UnitTestCase dispense de nettoyer explicitement la base après chaque test, ce qui explique pourquoi une donnée insérée disparaît sans suppression visible dans le code. Ce confort a deux limites précises, le DDL et les transactions manuelles imbriquées, qui méritent d’être identifiées avant qu’elles ne provoquent une suite de tests qui se pollue silencieusement d’une exécution à l’autre.

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