# Tester les actions AJAX de l’admin avec WP_Ajax_UnitTestCase

> Simuler des appels admin-ajax dans PHPUnit, capter les réponses JSON envoyées par wp_send_json, et vérifier nonce et capacité sur chaque action.

- Auteur : Clément Hadrot
- Publié le : 2021-05-07
- Mis à jour le : 2021-05-07
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/tester-actions-ajax-wp-ajax-unittestcase/

## L’essentiel

- WP_Ajax_UnitTestCase capte la sortie avant qu'elle ne parte au navigateur
- Une WPAjaxDieContinueException remplace le die réel
- Nonce et capacité doivent être testés séparément

Une action AJAX qui mettait à jour le statut d'une commande depuis l'écran d'administration fonctionnait bien en manuel, mais un correctif ultérieur avait fait sauter la vérification de nonce sans que personne ne s'en rende compte : la fonction utilisait encore `check_ajax_referer()`, mais avec un nom d'action qui ne correspondait plus à celui généré côté JavaScript. Résultat, l'action AJAX renvoyait une erreur pour tous les utilisateurs, y compris légitimes, en production. Un test d'intégration ciblé sur cette action aurait détecté l'incohérence en quelques secondes.

WordPress fournit une classe de test dédiée, `WP_Ajax_UnitTestCase`, qui simule un appel à `admin-ajax.php` sans navigateur ni serveur, en interceptant la sortie qu'une action AJAX enverrait normalement avec `wp_die()`.

## Pourquoi une classe de test dédiée aux actions AJAX

Une action AJAX se termine presque toujours par un appel à `wp_die()`, directement ou via `wp_send_json()`. Dans un contexte de test classique, cet appel interromprait immédiatement l'exécution du test lui-même. `WP_Ajax_UnitTestCase` remplace ce comportement par une exception dédiée, `WPAjaxDieContinueException`, capturée automatiquement pour permettre au test de continuer et d'inspecter la sortie générée.

## Écrire le test

```
class Test_Ajax_Statut_Commande extends WP_Ajax_UnitTestCase {

    public function test_met_a_jour_le_statut_pour_un_administrateur() {
        $admin_id   = self::factory()->user->create( array( 'role' => 'administrator' ) );
        $commande_id = self::factory()->post->create( array( 'post_type' => 'commande' ) );
        wp_set_current_user( $admin_id );

        $_POST['commande_id'] = $commande_id;
        $_POST['statut']      = 'expediee';
        $_POST['_ajax_nonce'] = wp_create_nonce( 'maj_statut_commande' );

        try {
            $this->_handleAjax( 'maj_statut_commande' );
        } catch ( WPAjaxDieContinueException $e ) {
            // Comportement attendu : wp_die() a été intercepté.
        }

        $reponse = json_decode( $this->_last_response, true );
        $this->assertTrue( $reponse['success'] );
    }
}
```

> L'essentiel à retenir : WP_Ajax_UnitTestCase capte la sortie avant qu'elle ne parte au navigateur ; Une WPAjaxDieContinueException remplace le die réel ; Nonce et capacité doivent être testés séparément

La propriété `$this->_last_response` contient exactement ce que `wp_send_json()` aurait envoyé au navigateur, prêt à être décodé et vérifié avec des assertions classiques sur la structure JSON.

## Tester le rejet pour nonce invalide

Le scénario symétrique, tout aussi important, consiste à vérifier qu'une requête sans nonce valide échoue proprement :

```
public function test_rejette_sans_nonce_valide() {
    $admin_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
    wp_set_current_user( $admin_id );

    $_POST['commande_id'] = 1;
    $_POST['_ajax_nonce'] = 'nonce-invalide';

    $this->setExpectedException( 'WPAjaxDieStopException' );
    $this->_handleAjax( 'maj_statut_commande' );
}
```

Notez la différence de classe d'exception : `WPAjaxDieStopException` signale un arrêt réel (mort du script), contrairement à `WPAjaxDieContinueException` utilisée pour la sortie JSON normale. Les deux servent à des scénarios de test distincts.

## Ne pas oublier le contrôle de capacité

Un nonce valide prouve que la requête vient bien du bon formulaire, pas que l'utilisateur a le droit d'agir. Les deux vérifications sont indépendantes et doivent être testées séparément :

- Un abonné avec un nonce valide doit être rejeté par `current_user_can()`
- Un administrateur avec un nonce invalide doit être rejeté par `check_ajax_referer()`
- Seule la combinaison des deux doit aboutir à un succès

## En résumé

WP_Ajax_UnitTestCase permet de couvrir des actions admin-ajax entières sans navigateur, en interceptant proprement le `wp_die()` final et en donnant accès à la réponse brute. Le raisonnement est proche de celui des routes REST personnalisées, mais admin-ajax reste un mécanisme distinct, plus ancien, avec ses propres classes de test et ses propres pièges de nonce.
