La suite WP_UnitTestCase est indispensable dès qu’un test touche réellement la base de données, la boucle WordPress ou le système de permissions. Mais une grande partie du code d’un plugin n’a rien à voir avec cela : des fonctions de calcul, de formatage, de validation, qui se contentent d’appeler apply_filters() ou get_option() en passant. Faire démarrer tout WordPress pour tester ce genre de logique est un gaspillage de temps, à raison de plusieurs secondes par exécution de suite.
Le paquet Brain Monkey, construit au-dessus de Mockery, résout ce problème en simulant les fonctions et hooks WordPress sans jamais charger le cœur. Voici comment l’intégrer à une suite PHPUnit existante et écrire des tests réellement isolés.
Pourquoi séparer logique métier et tests d’intégration
Un plugin bien structuré sépare généralement deux types de code : les fonctions qui orchestrent WordPress (enregistrement de hooks, requêtes WP_Query, écriture en base) et les fonctions qui contiennent la logique pure (calculs, transformations de données, règles métier). Les premières ont besoin d’un environnement WordPress réel pour être testées correctement, avec WP_UnitTestCase. Les secondes n’ont besoin que d’isoler les quelques appels WordPress qu’elles font, et Brain Monkey excelle précisément à cela.
Cette séparation, en plus d’accélérer les tests, pousse naturellement vers un code plus découplé : une fonction difficile à tester avec Brain Monkey parce qu’elle mélange trop d’appels WordPress et de logique est souvent une fonction qui gagnerait à être découpée.
Installer Brain Monkey
Le paquet s’installe via Composer, en dépendance de développement :
composer require --dev brain/monkey
Il embarque Mockery, la bibliothèque de mocks la plus utilisée en PHP, et fournit une API dédiée aux spécificités de WordPress : hooks, filtres, fonctions globales comme wp_die() ou __().

Configurer un TestCase dédié
Contrairement à WP_UnitTestCase, Brain Monkey s’utilise avec la classe PHPUnit\Framework\TestCase standard. Il faut initialiser et nettoyer son environnement dans setUp() et tearDown() :
use Brain\Monkey;
use PHPUnit\Framework\TestCase;
class Test_Calcul_Remise extends TestCase {
protected function setUp(): void {
parent::setUp();
Monkey\setUp();
}
protected function tearDown(): void {
Monkey\tearDown();
parent::tearDown();
}
}
Beaucoup d’équipes créent une classe abstraite Brain_Monkey_TestCase qui centralise ce setUp()/tearDown(), pour ne pas le répéter dans chaque classe de test.
Simuler une fonction WordPress avec Functions\when()
Supposons une fonction qui applique une remise et loggue le résultat via apply_filters() :
function mon_plugin_calcule_remise( $prix, $pourcentage ) {
$remise = $prix * ( $pourcentage / 100 );
return apply_filters( 'mon_plugin_remise_calculee', $prix - $remise, $prix, $pourcentage );
}
Pour tester cette fonction sans charger WordPress, Brain Monkey permet de définir ce que apply_filters() doit retourner :
use Brain\Monkey\Functions;
public function test_applique_la_remise_sans_filtre_actif() {
Functions\when( 'apply_filters' )->returnArg( 1 );
$resultat = mon_plugin_calcule_remise( 100, 10 );
$this->assertEquals( 90.0, $resultat );
}
Functions\when() convient pour les cas simples où l’on veut juste que la fonction WordPress se comporte de façon prévisible. Pour vérifier qu’une fonction WordPress est appelée avec des arguments précis, Functions\expect() est plus adapté.
Vérifier des appels avec Functions\expect()
public function test_declenche_le_filtre_avec_les_bons_arguments() {
Functions\expect( 'apply_filters' )
->once()
->with( 'mon_plugin_remise_calculee', 90.0, 100, 10 )
->andReturn( 90.0 );
$resultat = mon_plugin_calcule_remise( 100, 10 );
$this->assertEquals( 90.0, $resultat );
}
Ce test échoue si apply_filters() n’est pas appelé exactement une fois, avec exactement ces arguments, ce qui en fait un excellent outil pour documenter et verrouiller le contrat d’une fonction.
Tester hooks et actions
Brain Monkey propose aussi des espaces de noms dédiés aux actions et filtres pour tester qu’une fonction les enregistre correctement :
Actions\expectAdded( 'init' ): vérifie qu’un hook est bien accroché à l’actioninit.Filters\expectApplied( 'the_content' ): vérifie qu’un filtre donné est bien appliqué.Actions\expectDone( 'save_post' ): vérifie qu’une action a bien été déclenchée.
use Brain\Monkey\Actions;
public function test_enregistre_le_hook_init() {
Actions\expectAdded( 'init' )->once();
mon_plugin_enregistrer_hooks();
}
Les limites de l’approche
Brain Monkey ne remplace pas WP_UnitTestCase : il ne simule pas la base de données, ne recrée pas d’objets WP_Post réels, et ne convient pas pour tester une requête WP_Query complète. Utilisé à mauvais escient, il pousse aussi à écrire des tests qui vérifient l’implémentation plutôt que le comportement, en sur-spécifiant chaque appel de fonction.
Sur nos projets, nous réservons Brain Monkey aux classes de service et aux fonctions de calcul, et nous gardons
WP_UnitTestCasepour tout ce qui touche réellement à la persistance des données. Mélanger les deux approches dans une même classe de test crée en général plus de confusion que de clarté.
En résumé
Brain Monkey comble un vide entre les tests unitaires PHP classiques et les tests d’intégration lourds de WP_UnitTestCase : il permet d’isoler la logique métier d’un plugin des appels WordPress environnants, avec des suites nettement plus rapides à l’exécution. Combiné avec une architecture qui sépare clairement orchestration et logique pure, c’est l’un des meilleurs investissements pour garder une suite de tests agréable à faire tourner au quotidien.