2011-01-11 16 views
107

Ich versuche Jasmine zu verwenden, um einige BDD-Spezifikationen für grundlegende jQuery AJAX-Anforderungen zu schreiben. Ich verwende Jasmine derzeit im Standalone-Modus (d. H. Über SpecRunner.html). Ich habe SpecRunner konfiguriert, um jquery und andere .js Dateien zu laden. Irgendwelche Ideen, warum das Folgende nicht funktioniert? has_returned wird nicht wahr, dachte sogar das "Yuppi!" Alarm zeigt sich gut.Wie überprüfe ich jQuery AJAX Ereignisse mit Jasmine?

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); 
    }); 

}); 

Wie kann ich testen, dass der Rückruf aufgerufen wurde? Alle Hinweise auf Blogs/Material zum Testen von asynchronen jQuery mit Jasmine werden sehr geschätzt.

Antwort

12

Betrachten Sie das Jasmine-Ajax-Projekt: http://github.com/pivotal/jasmine-ajax.

Es ist ein Drop-in-Helfer, der (für jQuery oder Prototype.js) Stubs auf der XHR-Schicht, so dass Anfragen nie ausgehen. Sie können dann alles erwarten, was Sie über die Anfrage wollen.

Dann können Sie damit Befestigung Antworten für alle Fälle zur Verfügung stellen und dann für jede Antwort Tests schreiben, dass Sie wollen: Erfolg, Misserfolg, nicht autorisierte usw.

Es dauert Ajax-Aufrufe aus dem Reich der asynchronen Tests und Bietet Ihnen viel Flexibilität, um zu testen, wie Ihre tatsächlichen Antworthandler funktionieren sollten.

+0

Dank @jasminebdd, die Jasmin-Ajax-Projekt sieht aus wie die Art und Weise zum Testen meine js Code zu gehen. Aber was, wenn ich mit tatsächlichen Anforderungen an den Server testen wollte, z. für Konnektivitäts-/Integrationstests? – mnacos

+2

@mnacos Jasmine-Ajax ist vor allem nützlich für Komponententests, in denen Sie den Aufruf an den Server speziell vermeiden möchten. Wenn Sie Integrationstests durchführen, ist dies wahrscheinlich nicht das, was Sie wollen und sollte als separate Teststrategie entworfen werden. –

4

Wenn ich mit Jasmine einen Ajax-Code spezifiziere, löse ich das Problem, indem ich die abhängige Funktion ausspioniere, die den Remote-Aufruf initiiert (wie zB $ .get oder $ ajax). Dann erhalte ich die darauf gesetzten Callbacks und teste sie diskret.

Hier ist ein Beispiel, das ich vor kurzem gisted:

https://gist.github.com/946704

227

Ich denke, es gibt zwei Arten von Tests können Sie tun:

  1. Unit-Tests, dass gefälschte der AJAX-Request (Jasmins Spione verwenden) Damit können Sie Ihren gesamten Code testen, der kurz vor der AJAX-Anfrage und kurz danach läuft. Sie können sogar Jasmine verwenden, um eine Antwort vom Server zu fälschen. Diese Tests wären schneller - und sie müssten nicht mit asynchronem Verhalten umgehen -, da kein echtes AJAX stattfindet.
  2. Integrationstests, die echte AJAX-Anforderungen ausführen. Diese müssten asynchron sein.

Jasmine kann Ihnen helfen, beide Arten von Tests zu machen. Hier

ist ein Beispiel dafür, wie man gefälschte der AJAX-Request und dann einen Komponententest schreiben, um zu überprüfen, dass die gefälschten AJAX-Request an die richtige URL vorging:

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" 
    }); 
} 

Für Jasmin 2.stattdessen 0 Verwendung:

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

als

Hier in this answer bemerkt ist ein ähnliches Gerät zu testen, die Ihren Rückruf überprüft wurde ausgeführt, auf einer AJAX-Anforderung erfolgreichen Abschluss:

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 
    }); 
} 

Für Jasmine 2.0 stattdessen verwenden:

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

wie in this answer

Schließlich haben Sie an anderer Stelle angedeutet, dass Sie Integrationstests schreiben sollten, die echte AJAX-Anforderungen - für Integrationszwecke - machen. warten(), waitsFor() und läuft():

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 
    }); 
} 
+0

+1 Alex große Antwort. Tatsächlich hatte ich ein ähnliches Problem, für das ich eine Frage gestellt habe [Test-Ajax-Anfrage mit dem verzögerten Objekt] (http://stackoverflow.com/questions/12080087/test-ajax-request-using-the-sefered-object). Könnten Sie einen Blick darauf werfen? Vielen Dank. –

+12

Ich wünschte wirklich, deine Antwort wäre Teil der offiziellen Dokumentation für Jasmine. Sehr klare Antwort auf ein Problem, das mich seit ein paar Tagen umbringt. –

+0

Ausgezeichnete Antwort und Beispiel. Vielen Dank. –

0

Versuchen jqueryspy.com Es bietet eine elegante jquery wie Syntax Ihre Tests zu beschreiben und ermöglicht Rückrufe nach testen Dies kann asynchrone Funktionen mit Jasmins erfolgen der Ajax ist vollständig. Es ist ideal für Integrationstests und Sie können maximale Ajax-Wartezeiten in Sekunden oder Millisekunden konfigurieren.

5

hier ist ein einfaches Beispiel Testsuite für eine App js ähnliche

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

eine Probe-Test-Suite, die Callback-URL wie ich brauche, wird basierend auf 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(); 
}); 
}); 
+0

Danke, Alter. Dieses Beispiel hat mir sehr geholfen! Wenn Sie Jasmine> 2.0 verwenden, ist die Syntax für andCallFake spyOn (jQuery, "ajax") .und.callFake (/ * Ihre Funktion * /); –

0

Ich fühle mich anrufen um eine aktuellere Antwort zu geben, da Jasmine jetzt in der Version 2.4 ist und einige Funktionen gegenüber der Version 2.0 geändert wurden.

Um zu überprüfen, ob eine Callback-Funktion innerhalb Ihrer AJAX-Anfrage aufgerufen wurde, müssen Sie einen Spy erstellen, eine CallFake-Funktion hinzufügen und dann den Spy als Callback-Funktion verwenden. Hier ist, wie es geht:

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 
     }); 
    }); 
}); 

ich den Trick für den Erfolg Funktion getan haben sowie die Fehlerfunktion, um sicherzustellen, dass Jasmin wird die Spezifikationen so schnell wie möglich ausgeführt werden, auch wenn Ihre AJAX einen Fehler zurückgibt. Wenn Sie keine Fehlerfunktion angeben und Ihr AJAX einen Fehler zurückgibt, müssen Sie 5 Sekunden warten (Standard-Timeout-Intervall), bis Jasmine einen Fehler Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. ausgibt. Sie können auch Ihre eigenen Timeout wie folgt angeben:

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