# Tester vos hooks WordPress avec did_action, has_filter et des assertions ciblées

> Vérifier qu'une action se déclenche, qu'un filtre est enregistré et qu'il modifie bien la valeur attendue, sans bibliothèque tierce.

- Auteur : Clément Hadrot
- Publié le : 2020-11-13
- Mis à jour le : 2020-11-13
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/tester-hooks-did-action-has-filter/

## L’essentiel

- did_action compte les déclenchements réels
- has_filter confirme l'enregistrement
- apply_filters vérifie l'effet, pas juste la présence

Un correctif introduit par erreur un point-virgule en trop après un `add_filter()`, transformant l'appel en instruction morte et le filtre en simple commentaire silencieux. Le code compilait, aucune erreur PHP ne remontait, et le filtre censé arrondir les prix affichés ne s'exécutait tout simplement jamais. Un test qui vérifie qu'un hook est bien enregistré et bien déclenché aurait empêché ce genre de régression silencieuse de passer en revue de code.

WordPress expose trois fonctions simples pour ce genre de vérification : `has_action()` et `has_filter()` pour confirmer qu'un callback est accroché à un hook, et `did_action()` pour compter combien de fois une action donnée s'est réellement déclenchée pendant l'exécution. Combinées à des assertions classiques, elles couvrent l'essentiel des besoins sans dépendance externe.

## Vérifier qu'un callback est bien enregistré

La première question à se poser n'est pas « le filtre fonctionne-t-il ? » mais « le filtre est-il seulement accroché ? ». C'est le rôle de `has_filter()` et `has_action()` :

```
class Test_Hooks_Catalogue extends WP_UnitTestCase {

    public function test_le_filtre_de_prix_est_enregistre() {
        $priorite = has_filter( 'mon_plugin_prix_affiche', 'mon_plugin_arrondir_prix' );

        $this->assertNotFalse( $priorite );
        $this->assertSame( 20, $priorite );
    }
}
```

`has_filter()` retourne la priorité du callback, ou `false` s'il n'est pas enregistré. Vérifier la priorité elle-même a du sens quand l'ordre d'exécution compte, par exemple si deux extensions modifient la même valeur et que l'ordre change le résultat final.

## Compter les déclenchements réels avec did_action

Savoir qu'un callback est accroché ne dit rien sur son exécution effective. `did_action()` retourne le nombre de fois qu'une action a été déclenchée depuis le début de la requête, ce qui permet des assertions précises :

```
public function test_action_apres_creation_de_commande() {
    $this->assertSame( 0, did_action( 'mon_plugin_commande_creee' ) );

    mon_plugin_creer_commande( array( 'total' => 42.00 ) );

    $this->assertSame( 1, did_action( 'mon_plugin_commande_creee' ) );
}
```

> L'essentiel à retenir : did_action compte les déclenchements réels ; has_filter confirme l'enregistrement ; apply_filters vérifie l'effet, pas juste la présence

Ce comptage est particulièrement utile pour détecter les déclenchements en double, un bug fréquent quand un hook est accroché à la fois sur `save_post` et sur un hook spécifique comme `save_post_produit` sans garde-fou.

## Vérifier l'effet réel d'un filtre, pas seulement sa présence

La vérification la plus robuste reste d'appeler `apply_filters()` directement et de comparer l'entrée à la sortie, sans se soucier de savoir combien de callbacks sont accrochés :

```
public function test_le_prix_est_arrondi_au_centime_superieur() {
    $prix_arrondi = apply_filters( 'mon_plugin_prix_affiche', 19.994 );

    $this->assertSame( 20.00, $prix_arrondi );
}
```

Cette approche a un avantage : elle reste valide même si l'implémentation interne change de nom de fonction, tant que le contrat du filtre — sa signature d'entrée et de sortie — reste stable. C'est généralement le niveau de granularité le plus pertinent à tester en priorité.

## Tester le retrait d'un hook

Un cas fréquent sur les projets avec plusieurs extensions maison : vérifier qu'une extension désactive correctement un comportement du cœur ou d'une autre extension via `remove_action()` :

- Vérifier avant retrait que `has_action()` retourne bien une priorité
- Appeler le code responsable du retrait
- Vérifier après retrait que `has_action()` retourne `false`

Sans ce test, un changement de priorité par défaut dans une mise à jour du cœur ou d'une extension tierce peut faire échouer silencieusement le `remove_action()`, puisque WordPress exige que la priorité passée corresponde exactement à celle utilisée lors de l'enregistrement.

## En résumé

Avant de sortir l'artillerie lourde pour simuler des fonctions du cœur, ces trois fonctions natives couvrent déjà la majorité des besoins de test sur les hooks : présence avec `has_filter()`/`has_action()`, fréquence de déclenchement avec `did_action()`, et effet réel avec `apply_filters()`. Les bibliothèques de mock comme Brain Monkey ont leur place, mais pour un autre usage : tester du code qui appelle des fonctions WordPress sans charger le cœur du tout.
