Como posso verificar events do jQuery AJAX com o Jasmine?

Eu estou tentando usar o Jasmine para escrever algumas especificações do BDD para solicitações básicas do jQuery AJAX. Atualmente, estou usando o Jasmine no modo autônomo (por meio de SpecRunner.html ). Eu configurei o SpecRunner para carregar o jquery e outros arquivos .js. Alguma idéia porque o seguinte não funciona? não voltou a ser verdade, até pensei que o “yuppi!” alerta aparece bem.

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

Como faço para testar se o retorno de chamada foi chamado? Quaisquer sugestões para blogs / materiais relacionados ao teste jQuery asynchronous com Jasmine serão muito apreciados.

Eu acho que existem dois tipos de testes que você pode fazer:

  1. Testes de unidade que falsificam a solicitação AJAX (usando os espiões de Jasmine), permitindo que você teste todo o seu código que é executado logo antes da solicitação do AJAX, e só depois . Você pode até usar o Jasmine para falsificar uma resposta do servidor. Esses testes seriam mais rápidos – e não precisariam lidar com comportamento asynchronous – já que não há AJAX real em andamento.
  2. Testes de integração que executam solicitações AJAX reais. Estes precisariam ser asynchronouss.

Jasmine pode ajudá-lo a fazer os dois tipos de testes.

Aqui está uma amostra de como você pode falsificar a solicitação AJAX e, em seguida, escrever um teste de unidade para verificar se a solicitação falsificada do AJAX estava indo para a URL correta:

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

Para o Jasmine 2.0, use:

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

como observado nesta resposta

Aqui está um teste de unidade semelhante que verifica se o seu retorno de chamada foi executado, quando uma solicitação AJAX foi concluída com êxito:

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

Para o Jasmine 2.0, use:

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

como observado nesta resposta

Finalmente, você sugeriu em outro lugar que talvez queira escrever testes de integração que façam solicitações AJAX reais – para fins de integração. Isso pode ser feito usando os resources asynchronouss de Jasmine: waits (), waitsFor () e 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 }); } 

Veja o projeto jasmine-ajax: http://github.com/pivotal/jasmine-ajax .

É um auxiliar de drop-in que (para jQuery ou Prototype.js) stubs na camada XHR para que as solicitações nunca saiam. Você pode esperar tudo o que quiser sobre a solicitação.

Em seguida, ele permite que você forneça respostas fixas para todos os seus casos e, em seguida, escreva testes para cada resposta desejada: sucesso, falha, não autorizado etc.

Ele tira as chamadas do Ajax do domínio dos testes asynchronouss e fornece muita flexibilidade para testar como os manipuladores de resposta reais devem funcionar.

aqui está um conjunto de teste de exemplo simples para um aplicativo js como este

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

um conjunto de testes de amostra, que chamará o retorno de chamada com base na 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(); }); }); 

Quando eu especifico código ajax com Jasmine, eu resolvo o problema espionando qualquer function dependente que inicia a chamada remota (como por exemplo $ .get ou $ ajax). Então eu recupero os callbacks configurados e testo-os discretamente.

Aqui está um exemplo que eu gisted recentemente:

https://gist.github.com/946704

Tente jqueryspy.com Ele fornece uma jquery elegante como a syntax para descrever seus testes e permite que os retornos de chamada sejam testados após a conclusão do ajax. É ótimo para testes de integração e você pode configurar o tempo máximo de espera do ajax em segundos ou milésimos de segundo.

Eu sinto que preciso fornecer uma resposta mais atualizada, já que o Jasmine está agora na versão 2.4 e algumas funções mudaram da versão 2.0.

Portanto, para verificar se uma function de retorno de chamada foi chamada em sua solicitação AJAX, você precisa criar um espião, adicionar uma function callFake a ele e, em seguida, usar o espião como sua function de retorno de chamada. Veja como vai:

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

Eu fiz o truque para a function de sucesso, bem como a function de erro para garantir que o Jasmine execute as especificações o mais rápido possível, mesmo se o seu AJAX retornar um erro.

Se você não especificar uma function de erro e seu AJAX retornar um erro, será necessário aguardar 5 segundos (intervalo de tempo limite padrão) até que o Jasmine lance um erro. Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. . Você também pode especificar seu próprio tempo limite assim:

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