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


114

我正在尝试使用Jasmine为基本的jQuery AJAX请求编写一些BDD规范。我目前在独立模式下(即通过SpecRunner.html)使用Jasmine 。我已将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相关的博客/材料的指针将不胜感激。

Answers:


234

我猜您可以执行两种测试:

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

茉莉花可以帮助您进行两种测试。

这是如何伪造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");

本答案所述

这是一个类似的单元测试,可在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) {

本答案所述

最后,您在其他地方暗示您可能希望编写发出真实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 Alex很好的答案。实际上,我遇到了类似的问题,为此我使用Deferred对象打开了一个问题Test Ajax请求。你可以看看吗?谢谢。
罗兰·伯纳德

12
我真的希望您的回答是茉莉花官方文件的一部分。对已经杀死我几天的问题的非常明确的答案。
达伦·牛顿

4
此答案应确实标记为该问题的正式答案。
Thomas Amar

1
当前获取最新通话信息的方式是$ .ajax.calls.mostRecent()
camiblanch

1
您将如何为纯JS ajax实现它?
制表师

13

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

它是一个插入式帮助程序(对于jQuery或Prototype.js)在XHR层上存根,从而使请求永不中断。然后,您可以期望有关该请求的所有信息。

然后,您可以为所有案例提供夹具响应,然后针对所需的每个响应编写测试:成功,失败,未授权等。

它使Ajax调用脱离了异步测试的范围,并为您提供了很多灵活性来测试实际响应处理程序的工作方式。


感谢@ jasminebdd,jasmine-ajax项目看起来像测试我的js代码的方式。但是,如果我想测试对服务器的实际请求,例如连接/集成测试,该怎么办?
mnacos 2011年

2
@mnacos jasmine-ajax对于单元测试最有用,在这种情况下,您特别想避免调用服务器。如果要进行集成测试,则可能不是您想要的,应该将其设计为单独的测试策略。
塞巴斯蒂安·马丁

7

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

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

多谢,伙计。这个例子对我有很大帮助!请注意,如果您使用的是Jasmine> 2.0,则andCallFake的语法为spyOn(jQuery,“ ajax”)。and.callFake(/ *您的函数* /);
若昂·保罗莫塔

不知道这是版本的问题,但我得到了一个错误.andCallFake,用.and.callFake代替。谢谢。
OO

5

当我用Jasmine指定ajax代码时,我通过侦查任何依赖于远程启动的函数(例如$ .get或$ ajax)来解决此问题。然后,我检索设置在其上的回调并进行离散测试。

这是我最近摘录的一个示例:

https://gist.github.com/946704


0

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


0

我感觉我需要提供一个最新的答案,因为Jasmine现在是2.4版,并且一些功能已从2.0版更改。

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.