Mocker des appels ajax jQuery dans des tests JsTestDriver

Dans le précédent article sur les tests javascript, nous avons vu comment tester jQuery avec JsTestDriver. Le souci c’est lorsqu’une fonction va chercher des fragments de html, ou du json, ou d’autres données par ajax, dans ce cas l’appel de la fonction va réellement déclencher une requête http. En soi ce n’est pas un problème, on peut faire un test d’intégration en écrasant l’url de production vers une url en localhost.

Seulement, il y a 3 écueils :

  • avec JsTestDriver, l’url de base a changé (c’est celle du serveur de test qui est dynamique et pointe vers par exemple http://localhost:9876/slave/id/1311194023939/page/CONSOLE/mode/quirks/timeout/30000/upload_size/50/rt/CLIENT), donc il est nécessaire de faire une requête avec une url absolue (http://localhost/test/monJsonDeTest.json)
  • pour exécuter les tests il faut alors un serveur http en local avec la ressource déployée et le contexte configuré
  • avec JsTestDriver, on obtient une exception javascript (alors que le code de production fonctionne) que je ne suis pas arrivé à expliquer ou à contourner :
    [Exception... \"Component returned failure code: 0x80004005
    (NS_ERROR_FAILURE)\"  nsresult: \"0x80004005 (NS_ERROR_FAILURE)\"
    location: \"JS frame :: jquery.min.js\"

Un excellent livre : « Test Driven Javascript Development » donne une petite librairie (le code associé au chapitre 12) et des exemples pour mocker des appels ajax avec une couche de transport développée par Christian Johansen, l’auteur. J’ai appliqué la méthode qu’il propose à jQuery. Dans notre exemple, nous allons chercher un modèle en JSON, et nous vérifions que le html produit est conforme à ce qu’on attend :

TestCase("TestJsonAjax", {
    setUp: function() {
        // création du mock
        this.xhr = Object.create(fakeXMLHttpRequest);
        this.xhr.send=function () { readyState = 4; };
        this.xhr.getAllResponseHeaders=stubFn({});
        this.xhr.responseText="{'reponse':'réponse mockée'}";
        // masquage des fonctions jQuery
        jQuery.ajaxSettings.isLocal=stubFn(true);
        jQuery.ajaxSettings.xhr=stubFn(this.xhr);
    },
 
   "test getHtml() renvoie html bien formé": function () {
        var actualHtml;
 
        getHtml(function(html) {
            actualHtml = html;
        });
 
        assertEquals("
 
réponse mockée
 
", actualHtml);
    }
});

fakeXMLHttpRequest et la fonction stubFn() (crée une fonction qui renvoie la valeur fournie) sont dans la librairie tddjs (le repository git est http://tddjs.com/code/12-abstracting-browser-differences-ajax.git).

la ligne jQuery.ajaxSettings.xhr=stubFn(this.xhr) écrase la fonction de création de requête XMLHttpRequest de jQuery par celle définie dans le setUp (fakeXMLHttpRequest fournie par tddjs).

Le code qui fait passer le test :

function getHtml(resultFunc) {
   jQuery.getJSON('url/vers/le/modele/JSON', function(json) {
       resultFunc('
 
'+ json['reponse'] + '
 
');
   });
}
Cette entrée a été publiée dans javascript, tests, avec comme mot(s)-clef(s) , , . Vous pouvez la mettre en favoris avec ce permalien.

6 réponses à Mocker des appels ajax jQuery dans des tests JsTestDriver

  1. Philippe Blayo dit :

    A quoi correspond la valeur 4 du readyState qui masque xhr.send ?

    • Jean-Baptiste Potonnier dit :

      Voici ce que j’ai trouvé sur le site du W3C

      readyState of type unsigned short, readonly

      The state of the object. The attribute MUSt be one of the following values:

      0 Uninitialized
      The initial value.
      1 Open
      The open() method has been successfully called.
      2 Sent
      The UA successfully completed the request, but no data has yet been received.
      3 Receiving
      Immediately before receiving the message body (if any). All HTTP headers have been received.
      4 Loaded
      The data transfer has been completed.

  2. Bruno Thomas dit :

    Merci JB pour les valeurs readyState. En effet dans la closure ajaxTransport, il y a le code qui appelle les callbacks, qui dépend de l’état readyState. Si la valeur ne vaut pas « loaded » (4), le callback n’est pas appelé.

    if ( isAbort || !script.readyState || 
         /loaded|complete/.test( script.readyState ) ) {
      // (...) code 
      if ( !isAbort ) {
        callback( 200, "success" );
      }
    }
  3. Philippe Blayo dit :

    Et this.xhr limite la portée de xhr à la classe de test… Sinon ce serait une variable globale.

  4. Xavier Nopre dit :

    Bonjour,

    Je découvre ce blog qui m’intéresse, pas le temps de lire tout de suite, mais je voudrais ajouter un suivi RSS dans mon reader : aïe, je ne trouve rien, dommage ….

    Bon, je vais contacter l’auteur : re-aïe, un page « A propos » avec la liste des auteurs qui renvoie vers …. les articles, aucun moyen de vous contacter autrement que par ce commentaire.

    Donc désolé pour cette prise de contact détournée, mes remarques ci-dessus et merci pour vos réponses ;-)

    Xavier

    • Bonjour,

      merci pour cette remarque : auparavant, Firefox et consorts proposaient une icône RSS dans la barre d’adresse. Je vais rajouter un lien vers les articles, mais vous la donne d’ores et déjà : http://www.barreverte.fr/feed (bon, j’ai du regarder dans le source de la page…)
      Pour le contact, je vais mettre à jour la page « A propos » avec un e-mail de contact.

      A bientôt !
      JP

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>