web-dev-qa-db-fra.com

Comment vérifier jQuery AJAX événements avec Jasmine?

J'essaie d'utiliser Jasmine pour écrire certaines spécifications BDD pour les requêtes jQuery AJAX de base. J'utilise actuellement Jasmine en mode autonome (c'est-à-dire par le biais de SpecRunner.html). J'ai configuré SpecRunner pour charger jquery et d'autres fichiers .js. Des idées pourquoi le suivant ne fonctionne pas? has_returned ne devient pas vrai, même pensé le "yuppi!" alerte montre bien.

describe("A jQuery ajax request should be able to fetch...", function() {

  it("an XML file from the filesystem", function() {
    $.ajax_get_xml_request = { has_returned : false };  
    // initiating the AJAX request
    $.ajax({ type: "GET", url: "addressbook_files/addressbookxml.xml", dataType: "xml",
             success: function(xml) { alert("yuppi!"); $.ajax_get_xml_request.has_returned = true; } }); 
    // waiting for has_returned to become true (timeout: 3s)
    waitsFor(function() { $.ajax_get_xml_request.has_returned; }, "the JQuery AJAX GET to return", 3000);
    // TODO: other tests might check size of XML file, whether it is valid XML
    expect($.ajax_get_xml_request.has_returned).toEqual(true);
  }); 

});

Comment puis-je tester que le rappel a été appelé? Tout pointeur sur les blogs/matériel lié au test asynchrone de jQuery avec Jasmine sera grandement apprécié.

112
mnacos

Je suppose qu'il y a deux types de tests que vous pouvez faire:

  1. Tests unitaires simulant la demande AJAX (à l'aide d'espions de Jasmine), vous permettant de tester tout votre code exécuté sous juste avant la demande AJAX, et juste après. Vous pouvez même utiliser Jasmine pour simuler une réponse du serveur. Ces tests seraient plus rapides (et ils n'auraient pas besoin de gérer le comportement asynchrone) car il n'y a pas de réel AJAX.
  2. Tests d'intégration exécutant de véritables requêtes AJAX. Ceux-ci devraient être asynchrones.

Jasmine peut vous aider à faire les deux types de tests.

Voici un exemple de la façon dont vous pouvez simuler la demande AJAX, puis écrire un test unitaire pour vérifier que la demande fictive AJAX était dirigée vers l'URL correcte:

it("should make an AJAX request to the correct URL", function() {
    spyOn($, "ajax");
    getProduct(123);
    expect($.ajax.mostRecentCall.args[0]["url"]).toEqual("/products/123");
});

function getProduct(id) {
    $.ajax({
        type: "GET",
        url: "/products/" + id,
        contentType: "application/json; charset=utf-8",
        dataType: "json"
    });
}

Pour Jasmine 2.0, utilisez plutôt:

expect($.ajax.calls.mostRecent().args[0]["url"]).toEqual("/products/123");

comme indiqué dans cette réponse

Voici un test unitaire similaire qui vérifie que votre rappel a été exécuté, à l'issue d'une demande AJAX terminée avec succès:

it("should execute the callback function on success", function () {
    spyOn($, "ajax").andCallFake(function(options) {
        options.success();
    });
    var callback = jasmine.createSpy();
    getProduct(123, callback);
    expect(callback).toHaveBeenCalled();
});

function getProduct(id, callback) {
    $.ajax({
        type: "GET",
        url: "/products/" + id,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: callback
    });
}

Pour Jasmine 2.0, utilisez plutôt:

spyOn($, "ajax").and.callFake(function(options) {

comme indiqué dans cette réponse

Enfin, vous avez laissé entendre ailleurs que vous souhaiteriez peut-être écrire des tests d'intégration faisant de vraies requêtes AJAX, à des fins d'intégration. Cela peut être fait en utilisant les fonctionnalités asynchrones de Jasmine: waits (), waitsFor () et runs ():

it("should make a real AJAX request", function () {
    var callback = jasmine.createSpy();
    getProduct(123, callback);
    waitsFor(function() {
        return callback.callCount > 0;
    });
    runs(function() {
        expect(callback).toHaveBeenCalled();
    });
});

function getProduct(id, callback) {
    $.ajax({
        type: "GET",
        url: "data.json",
        contentType: "application/json; charset=utf-8"
        dataType: "json",
        success: callback
    });
}
230
Alex York

Regardez le projet jasmine-ajax: http://github.com/pivotal/jasmine-ajax .

Il s'agit d'un assistant immédiat (que ce soit pour jQuery ou Prototype.js) au niveau de la couche XHR afin que les demandes ne soient jamais envoyées. Vous pouvez alors attendre tout ce que vous voulez de la demande.

Ensuite, il vous permet de fournir des réponses fixes pour tous vos cas, puis d’écrire des tests pour chaque réponse souhaitée: succès, échec, non autorisé, etc.

Il permet aux appels Ajax de sortir du domaine des tests asynchrones et vous offre une grande flexibilité pour tester le fonctionnement de vos gestionnaires de réponses réels.

13
user533109

voici un exemple de suite de tests simple pour une application comme celle-ci

var app = {
               fire: function(url, sfn, efn) {
                   $.ajax({
                       url:url,
                       success:sfn,
                       error:efn
                   });
                }
         };

un exemple de suite de tests, qui appellera un rappel basé sur l'URL regexp

describe("ajax calls returns", function() {
 var successFn, errorFn;
 beforeEach(function () {
    successFn = jasmine.createSpy("successFn");
    errorFn = jasmine.createSpy("errorFn");
    jQuery.ajax = spyOn(jQuery, "ajax").andCallFake(
      function (options) {
          if(/.*success.*/.test(options.url)) {
              options.success();
          } else {
              options.error();
          }
      }
    );
 });

 it("success", function () {
     app.fire("success/url", successFn, errorFn);
     expect(successFn).toHaveBeenCalled();
 });

 it("error response", function () {
     app.fire("error/url", successFn, errorFn);
     expect(errorFn).toHaveBeenCalled();
 });
});
7
skipy

Lorsque je spécifie du code ajax avec Jasmine, je résous le problème en espionnant la fonction dépendante qui initie l'appel distant (comme, par exemple, $ .get ou $ ajax). Ensuite, je récupère les callbacks définis dessus et les teste discrètement.

Voici un exemple que j'ai récemment expliqué:

https://Gist.github.com/946704

5
Justin Searls

Essayez jqueryspy.comIl fournit une syntaxe élégante, semblable à celle de jquery, pour décrire vos tests et permet aux callbacks de tester une fois l’ajustement terminé. Il est idéal pour les tests d’intégration et vous pouvez configurer des temps d’attente ajax maximum en secondes ou en millisecondes.

0
Steve Toms

J'ai le sentiment que je dois fournir une réponse plus à jour puisque Jasmine est maintenant à la version 2.4 et que quelques fonctions ont été modifiées par rapport à la version 2.0.

Ainsi, pour vérifier qu'une fonction de rappel a été appelée dans votre demande AJAX, vous devez créer un espion, y ajouter une fonction callFake, puis utiliser l'espion comme fonction de rappel. Voici comment ça se passe:

describe("when you make a jQuery AJAX request", function()
{
    it("should get the content of an XML file", function(done)
    {
        var success = jasmine.createSpy('success');
        var error = jasmine.createSpy('error');

        success.and.callFake(function(xml_content)
        {
            expect(success).toHaveBeenCalled();

            // you can even do more tests with xml_content which is
            // the data returned by the success function of your AJAX call

            done(); // we're done, Jasmine can run the specs now
        });

        error.and.callFake(function()
        {
            // this will fail since success has not been called
            expect(success).toHaveBeenCalled();

            // If you are happy about the fact that error has been called,
            // don't make it fail by using expect(error).toHaveBeenCalled();

            done(); // we're done
        });

        jQuery.ajax({
            type : "GET",
            url : "addressbook_files/addressbookxml.xml",
            dataType : "xml",
            success : success,
            error : error
        });
    });
});

J'ai fait le tour pour la fonction de succès ainsi que la fonction d'erreur pour m'assurer que Jasmine exécutera les spécifications le plus rapidement possible, même si votre AJAX renvoie une erreur.

Si vous ne spécifiez pas de fonction d'erreur et que votre AJAX renvoie une erreur, vous devrez attendre 5 secondes (intervalle de délai d'attente par défaut) jusqu'à ce que Jasmine génère une erreur Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.. Vous pouvez également spécifier votre propre délai d'attente comme ceci:

it("should get the content of an XML file", function(done)
{
    // your code
},
10000); // 10 seconds
0
pmrotule