vendredi 25 septembre 2026

À propos

Contact

Tests

WP_UnitTestCase : maîtriser les factories pour des tests WordPress fiables

Créer un post, un utilisateur ou un terme de taxonomie ne devrait jamais polluer votre base de test. Voici comment les factories de WP_UnitTestCase rendent vos tests rapides et reproductibles.

Par Clément Hadrot • 19 mai 2020 • 5 min de lecture • Aucun commentaire
WP_UnitTestCase : maîtriser les factories pour des tests WordPress fiables

Un test qui dépend de données déjà présentes dans la base n’est pas un test, c’est un pari. Si le contenu change, si un autre développeur supprime un article, si l’ordre d’exécution des tests varie, le résultat devient imprévisible. WP_UnitTestCase résout ce problème avec un mécanisme de factories qui crée des objets WordPress à la demande, et les annule proprement une fois le test terminé.

Comprendre ces factories change radicalement la façon d’écrire des tests sur WordPress : au lieu de préparer manuellement un jeu de données fragile, vous décrivez dans chaque test exactement ce dont il a besoin, ni plus ni moins. Voici comment les utiliser efficacement, avec les pièges à éviter.

Le principe : une transaction annulée à chaque test

Avant chaque méthode de test, WP_UnitTestCase ouvre une transaction MySQL. Tout ce que vous créez pendant le test, posts, utilisateurs, options, termes de taxonomie, est inséré normalement en base. Mais à la fin du test, la classe exécute un ROLLBACK qui annule tout. Résultat concret : votre base de test reste vide entre deux tests, sans qu’il soit nécessaire d’écrire le moindre code de nettoyage.

C’est ce mécanisme qui autorise à créer librement des contenus dans vos tests sans craindre de polluer les suivants. C’est aussi la raison pour laquelle les tests basés sur WP_UnitTestCase ne conviennent pas à des scénarios qui dépendent explicitement de transactions MySQL applicatives : le rollback global de la classe de test entrerait en conflit.

Créer des données avec factory()

La méthode $this->factory() expose un ensemble d’objets qui savent créer chaque type de contenu WordPress. Les plus utilisés :

  • $this->factory()->post->create( $args ) : crée un article et retourne son ID.
  • $this->factory()->post->create_and_get( $args ) : crée un article et retourne directement l’objet WP_Post.
  • $this->factory()->user->create( $args ) : crée un utilisateur.
  • $this->factory()->term->create( $args ) : crée un terme de taxonomie.
  • $this->factory()->comment->create( $args ) : crée un commentaire.
  • $this->factory()->attachment->create_upload_object( $chemin_fichier ) : crée une pièce jointe à partir d’un fichier réel.
L'essentiel à retenir : Des données de test créées et détruites à chaque test ; Une syntaxe unique pour posts, users, terms et commentaires ; go_to() pour simuler une vraie requête front

Exemple : tester une fonction qui filtre des articles par auteur

Imaginons une fonction mon_plugin_get_articles_publies_par( $user_id ) qui retourne uniquement les articles publiés d’un auteur donné. Un test complet ressemble à ceci :

class Test_Articles_Par_Auteur extends WP_UnitTestCase {

    public function test_ne_retourne_que_les_articles_publies() {
        $auteur = $this->factory()->user->create( array(
            'role' => 'author',
        ) );

        $publie = $this->factory()->post->create( array(
            'post_author' => $auteur,
            'post_status' => 'publish',
        ) );

        $brouillon = $this->factory()->post->create( array(
            'post_author' => $auteur,
            'post_status' => 'draft',
        ) );

        $resultats = mon_plugin_get_articles_publies_par( $auteur );

        $this->assertContains( $publie, $resultats );
        $this->assertNotContains( $brouillon, $resultats );
    }
}

Ce test ne dépend d’aucune donnée préexistante : il crée exactement ce dont il a besoin, exécute la fonction testée, puis vérifie le résultat. À la fin du test, l’auteur et les deux articles disparaissent grâce au rollback.

Créer plusieurs objets d’un coup

Pour des scénarios nécessitant plusieurs contenus similaires, create_many() évite de répéter les appels :

$ids = $this->factory()->post->create_many( 5, array(
    'post_status' => 'publish',
) );

$this->assertCount( 5, $ids );

Simuler une requête front avec go_to()

Certaines fonctions dépendent du contexte de la requête WordPress en cours, par exemple is_single(), get_queried_object() ou une fonction personnalisée qui inspecte $wp_query. La méthode $this->go_to( $url ) simule une navigation vers une URL donnée et reconstruit l’objet WP_Query en conséquence :

public function test_is_single_sur_un_article_publie() {
    $post_id = $this->factory()->post->create();
    $this->go_to( get_permalink( $post_id ) );

    $this->assertTrue( is_single() );
    $this->assertEquals( $post_id, get_queried_object_id() );
}

Sans go_to(), is_single() retournerait systématiquement false dans un test, puisque aucune requête réelle n’a été effectuée.

Assertions spécifiques à WordPress

WP_UnitTestCase ajoute des assertions au-delà de celles de PHPUnit, notamment :

  • assertWPError( $actual ) : vérifie qu’une valeur est bien un objet WP_Error.
  • assertQueryTrue( ...$conditions ) : vérifie qu’un ensemble précis de fonctions conditionnelles de la boucle (is_home, is_archive, etc.) retournent true, et que toutes les autres retournent false.
  • assertEqualSets( $expected, $actual ) : compare deux tableaux sans tenir compte de l’ordre des éléments, pratique pour comparer des listes d’IDs.

Sur nos projets, nous évitons de créer des données dans une méthode setUp() partagée par toute une classe de test dès que ces données ne servent pas à tous les tests de la classe. Un setUp() trop chargé rend chaque test plus lent, et surtout plus difficile à comprendre isolément.

En résumé

Les factories de WP_UnitTestCase transforment l’écriture de tests WordPress en un exercice rapide et lisible : chaque test décrit ses propres données, sans effet de bord sur les suivants. Combinées à go_to() pour le contexte de requête et aux assertions spécifiques comme assertWPError(), elles couvrent la grande majorité des besoins de test d’un plugin ou d’un thème. La prochaine étape, pour les tests qui n’ont pas besoin du cœur WordPress, consiste à explorer des outils plus légers comme Brain Monkey.

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