vendredi 25 septembre 2026

À propos

Contact

Tests

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.

Par Clément Hadrot • 7 mai 2021 • 3 min de lecture • Aucun commentaire
Tester les actions AJAX de l'admin avec WP_Ajax_UnitTestCase

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.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi