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

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 :
AssertionsArticleTraitpour tout ce qui touche aux contenus publiés.AssertionsReponseRestTraitpour la structure des réponses d’API.AssertionsUtilisateurTraitpour 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.