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

Tests

PHPUnit : un trait d’assertions maison, sans hériter d’une classe abstraite

Répéter la même vérification dans dix classes de test alourdit le code. Un trait PHP la centralise sans imposer de hiérarchie d'héritage rigide.

Par WordPress Développement • 20 mai 2020 • 4 min de lecture • Aucun commentaire
PHPUnit : un trait d'assertions maison, sans hériter d'une classe abstraite

Une agence qui gère plusieurs extensions WordPress finit toujours par écrire la même vérification à plusieurs endroits : contrôler qu’un article possède bien un certain méta, que sa taxonomie est correctement renseignée, ou qu’une réponse REST respecte un format précis. Copiée d’une classe de test à l’autre, cette vérification devient vite une source d’incohérences : une version légèrement modifiée dans un fichier, oubliée dans un autre.

La tentation naturelle consiste à créer une classe abstraite commune dont toutes les classes de test hériteraient. Mais PHP n’autorise qu’un seul héritage, et cette classe abstraite entre alors en concurrence avec WP_UnitTestCase ou TestCase, dont chaque classe de test a déjà besoin. Un trait PHP contourne cette limite proprement.

Le problème de la hiérarchie unique

Dès qu’une classe de test hérite de WP_UnitTestCase, elle ne peut plus hériter d’une autre classe. Introduire une classe abstraite intermédiaire, par exemple Mon_Assertions_Abstraites, imposerait de faire hériter cette classe elle-même de WP_UnitTestCase, ce qui fonctionne, mais rigidifie l’architecture : toute nouvelle famille de tests ayant besoin d’un autre socle se retrouve bloquée.

// Fonctionne, mais fige toute la hiérarchie de test
abstract class Mon_Assertions_Abstraites extends WP_UnitTestCase {
    protected function assertArticlePublie( int $post_id ): void {
        $statut = get_post_status( $post_id );
        $this->assertSame( 'publish', $statut );
    }
}

Un trait règle ce problème en apportant des méthodes à une classe sans imposer de lien de parenté. Il peut être mélangé (« use ») dans n’importe quelle classe de test, quelle que soit sa classe parente réelle.

Écrire le trait

L'essentiel à retenir : Une assertion réutilisable sans classe parente imposée ; Compatible avec un seul héritage déjà utilisé ; S'applique classe par classe, à la demande
trait AssertionsArticleTrait {

    protected function assertArticlePublie( int $post_id ): void {
        $statut = get_post_status( $post_id );
        $this->assertSame(
            'publish',
            $statut,
            "L'article {$post_id} devrait être publié, statut actuel : {$statut}"
        );
    }

    protected function assertArticlePossedeMeta( int $post_id, string $cle, $valeur_attendue ): void {
        $valeur = get_post_meta( $post_id, $cle, true );
        $this->assertSame( $valeur_attendue, $valeur );
    }
}

Ce trait ne contient aucune logique de préparation ni de nettoyage, uniquement des méthodes d’assertion. Il suppose que la classe qui l’utilise possède déjà les méthodes assertSame fournies par PHPUnit, ce qui est le cas dès que cette classe hérite, directement ou indirectement, de PHPUnit\Framework\TestCase.

L’utiliser dans une classe de test

class ArticlePublicationTest extends WP_UnitTestCase {

    use AssertionsArticleTrait;

    public function test_publication_directe(): void {
        $post_id = self::factory()->post->create( [ 'post_status' => 'publish' ] );
        $this->assertArticlePublie( $post_id );
    }

    public function test_meta_langue_correcte(): void {
        $post_id = self::factory()->post->create();
        update_post_meta( $post_id, '_langue', 'fr' );
        $this->assertArticlePossedeMeta( $post_id, '_langue', 'fr' );
    }
}

La classe garde son héritage naturel de WP_UnitTestCase, tout en gagnant deux méthodes d’assertion supplémentaires par simple import du trait. Une autre classe de test, dans un contexte n’ayant rien à voir avec les articles, peut utiliser ce même trait sans aucune contrainte de hiérarchie.

Ce que le trait ne remplace pas

Un trait ne peut pas définir de constructeur au sens strict, ni gérer de logique de préparation partagée entre plusieurs traits utilisés simultanément de façon fiable : deux traits définissant une méthode setUp entrent en conflit et doivent être résolus explicitement avec la syntaxe insteadof. Pour de la préparation de données complexe, une factory ou une méthode utilitaire appelée explicitement depuis setUp reste plus lisible qu’un trait surchargé de responsabilités.

Un trait d’assertions doit rester ennuyeux : des vérifications courtes, sans effet de bord, qui décrivent ce qu’on attend plutôt que comment le préparer.

Organiser plusieurs traits par domaine

Sur un projet volumineux, un seul trait fourre-tout devient vite difficile à maintenir. Le découper par domaine fonctionnel, un trait pour les articles, un autre pour les commandes WooCommerce, un troisième pour les réponses REST, garde chaque fichier court et permet à une classe de test de n’importer que ce dont elle a réellement besoin :

  • AssertionsArticleTrait pour tout ce qui touche aux contenus publiés.
  • AssertionsReponseRestTrait pour la structure des réponses d’API.
  • AssertionsUtilisateurTrait pour les rôles et capacités.

En résumé

Un trait PHP offre exactement ce qu’une classe abstraite commune ne peut pas donner sans compromis : des assertions réutilisables, importables à la demande, sans jamais entrer en concurrence avec l’héritage déjà occupé par WP_UnitTestCase. Pour une agence qui gère plusieurs projets avec des vérifications récurrentes, c’est le mécanisme le plus simple à mettre en place et le moins coûteux à faire évoluer.

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