如何使用Jasmine验证jQuery AJAX事件?

时间:2011-01-11 21:04:57

标签: javascript jquery ajax jasmine bdd

我正在尝试使用Jasmine为基本的jQuery AJAX请求编写一些BDD规范。我目前在独立模式下使用Jasmine(即通过SpecRunner.html)。我已配置SpecRunner来加载jquery和其他.js文件。任何想法为什么以下不起作用? has_returned并不成真,甚至想到了“yuppi!”警报显示正常。

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

});

如何测试是否已调用回调?任何与使用Jasmine测试异步jQuery相关的博客/材料的指针都将非常感激。

6 个答案:

答案 0 :(得分:230)

我想你可以做两种类型的测试:

  1. 伪造AJAX请求的单元测试(使用Jasmine的间谍),使您能够测试在 AJAX请求之前运行的所有代码,然后 。您甚至可以使用Jasmine伪造来自服务器的响应。这些测试会更快 - 而且他们不需要处理异步行为 - 因为没有任何真正的AJAX正在进行。
  2. 执行真实AJAX请求的集成测试。这些都需要是异步的。
  3. Jasmine可以帮助您进行两种测试。

    以下是如何伪造AJAX请求,然后编写单元测试以验证伪造的AJAX请求是否转到正确的URL的示例:

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

    对于 Jasmine 2.0 使用:

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

    this answer

    中所述

    这是一个类似的单元测试,用于在AJAX请求成功完成后验证您的回调是否已执行:

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

    对于 Jasmine 2.0 使用:

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

    this answer

    中所述

    最后,您已在其他地方暗示您可能希望编写实现真正AJAX请求的集成测试 - 用于集成目的。这可以使用Jasmine的异步功能来完成:waits(),waitsFor()和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
        });
    }
    

答案 1 :(得分:13)

查看jasmine-ajax项目:http://github.com/pivotal/jasmine-ajax

它是XHR层(对于jQuery或Prototype.js)存根的一个直接帮助,因此请求永远不会消失。然后,您可以期待所有关于请求的信息。

然后,它允许您为所有案例提供夹具响应,然后为您想要的每个响应编写测试:成功,失败,未授权等。

它将Ajax调用排除在异步测试领域之外,并为测试实际响应处理程序的工作方式提供了很大的灵活性。

答案 2 :(得分:7)

这是一个简单的示例测试套件 对于像这样的应用程序

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

示例测试套件,它将根据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();
 });
});

答案 3 :(得分:5)

当我用Jasmine指定ajax代码时,我通过监视任何依赖的函数启动远程调用来解决问题(例如,$ .get或$ ajax)。然后我检索它上面设置的回调并分别测试它们。

以下是我最近提出的一个例子:

https://gist.github.com/946704

答案 4 :(得分:0)

试试jqueryspy.com 它提供了一个优雅的jquery语法来描述你的测试,并允许在ajax完成后进行回调测试。它非常适合集成测试,您可以配置最大ajax等待时间,以秒或毫秒为单位。

答案 5 :(得分:0)

我觉得我需要提供一个更新的答案,因为Jasmine现在的版本为2.4,而且一些功能已经从版本2.0发生了变化。

因此,要验证在AJAX请求中是否已调用回调函数,您需要创建一个间谍,向其添加一个callFake函数,然后使用spy作为您的回调函数。这是怎么回事:

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

我已经完成了成功函数和错误函数的技巧,以确保即使你的AJAX返回错误,Jasmine也会尽快运行规范。

如果未指定错误函数且AJAX返回错误,则必须等待5秒(默认超时间隔),直到Jasmine抛出错误Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.。您也可以像这样指定自己的超时:

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