Làm cách nào để xác minh các sự kiện jQuery AJAX với Jasmine?


114

Tôi đang cố gắng sử dụng Jasmine để viết một số thông số kỹ thuật BDD cho các yêu cầu jQuery AJAX cơ bản. Tôi hiện đang sử dụng Jasmine ở chế độ độc lập (tức là thông qua SpecRunner.html). Tôi đã định cấu hình SpecRunner để tải jquery và các tệp .js khác. Bất kỳ ý tưởng tại sao sau đây không hoạt động? has_returned không trở thành sự thật, thậm chí nghĩ rằng "yuppi!" cảnh báo hiển thị tốt.

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

});

Làm cách nào để kiểm tra xem cuộc gọi lại đã được gọi chưa? Bất kỳ con trỏ nào đến blog / tài liệu liên quan đến thử nghiệm jQuery không đồng bộ với Jasmine sẽ được đánh giá rất cao.

Câu trả lời:


234

Tôi đoán có hai loại kiểm tra bạn có thể làm:

  1. Kiểm tra đơn vị giả mạo yêu cầu AJAX (sử dụng gián điệp của Jasmine), cho phép bạn kiểm tra tất cả mã của mình chạy ngay trước yêu cầu AJAX và ngay sau đó . Bạn thậm chí có thể sử dụng Jasmine để giả mạo phản hồi từ máy chủ. Các thử nghiệm này sẽ nhanh hơn - và chúng sẽ không cần phải xử lý hành vi không đồng bộ - vì không có bất kỳ AJAX thực nào đang diễn ra.
  2. Kiểm tra tích hợp thực hiện các yêu cầu AJAX thực. Chúng cần phải không đồng bộ.

Jasmine có thể giúp bạn làm cả hai loại xét nghiệm.

Dưới đây là một ví dụ về cách bạn có thể giả mạo yêu cầu AJAX và sau đó viết một bài kiểm tra đơn vị để xác minh rằng yêu cầu AJAX giả mạo đã đi đến đúng 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"
    });
}

Để sử dụng Jasmine 2.0 thay thế:

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

như đã lưu ý trong câu trả lời này

Đây là một bài kiểm tra đơn vị tương tự để xác minh rằng lệnh gọi lại của bạn đã được thực thi, khi yêu cầu AJAX hoàn tất thành công:

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

Để sử dụng Jasmine 2.0 thay thế:

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

như đã lưu ý trong câu trả lời này

Cuối cùng, bạn đã gợi ý ở những nơi khác rằng bạn có thể muốn viết các bài kiểm tra tích hợp thực hiện các yêu cầu AJAX thực sự - cho các mục đích tích hợp. Điều này có thể được thực hiện bằng cách sử dụng các tính năng không đồng bộ của Jasmine: waits (), waitsFor () và running ():

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 câu trả lời tuyệt vời của Alex. Trên thực tế, tôi đã gặp phải một vấn đề tương tự mà tôi đã mở một câu hỏi Kiểm tra yêu cầu Ajax bằng cách sử dụng đối tượng Deferred . Bạn có thể xem qua? cảm ơn.
Lorraine Bernard

12
Tôi thực sự ước câu trả lời của bạn là một phần của tài liệu chính thức cho Jasmine. Câu trả lời rất rõ ràng cho một vấn đề đã giết tôi trong vài ngày.
Darren Newton

4
Câu trả lời này thực sự nên được đánh dấu là câu trả lời chính thức cho câu hỏi này.
Thomas Amar

1
Cách hiện tại để nhận thông tin cuộc gọi gần đây nhất là $ .ajax.calls.mostRecent ()
camiblanch

1
Bạn sẽ triển khai điều đó như thế nào cho JS ajax đơn giản?
Thợ đồng hồ

13

Ngắm nhìn dự án jasmine-ajax: http://github.com/pivotal/jasmine-ajax .

Đó là một trình trợ giúp thả vào (cho jQuery hoặc Prototype.js) khai thác ở lớp XHR để các yêu cầu không bao giờ xuất hiện. Sau đó, bạn có thể mong đợi tất cả những gì bạn muốn về yêu cầu.

Sau đó, nó cho phép bạn cung cấp các phản hồi cố định cho tất cả các trường hợp của bạn và sau đó viết các bài kiểm tra cho từng phản hồi mà bạn muốn: thành công, thất bại, trái phép, v.v.

Việc gọi Ajax ra khỏi lĩnh vực kiểm tra không đồng bộ và cung cấp cho bạn nhiều tính linh hoạt để kiểm tra cách thức hoạt động của các trình xử lý phản hồi thực tế của bạn.


Cảm ơn @jasminebdd, dự án jasmine-ajax giống như một cách để thử nghiệm mã js của tôi. Nhưng điều gì sẽ xảy ra nếu tôi muốn kiểm tra với các yêu cầu thực tế đến máy chủ, ví dụ: kiểm tra kết nối / tích hợp?
mnacos

2
@mnacos jasmine-ajax chủ yếu hữu ích cho việc kiểm tra đơn vị trong trường hợp bạn muốn tránh lệnh gọi đến máy chủ. Nếu bạn đang thực hiện kiểm tra tích hợp, đây có thể không phải là điều bạn muốn và nên được thiết kế như một chiến lược kiểm tra riêng biệt.
Sebastien Martin

7

đây là một bộ thử nghiệm ví dụ đơn giản cho một ứng dụng js như thế này

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

một bộ thử nghiệm mẫu, sẽ gọi lại dựa trên 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();
 });
});

Cảm ơn anh bạn. Ví dụ này đã giúp tôi rất nhiều! Chỉ cần lưu ý rằng nếu bạn đang sử dụng Jasmine> 2.0 thì cú pháp cho andCallFake là spyOn (jQuery, "ajax"). And.callFake (/ * hàm của bạn * /);
João Paulo Motta

Không chắc đây có phải là sự cố phiên bản hay không nhưng tôi đã gặp lỗi khi bật .andCallFake, đã sử dụng .and.callFakethay thế. Cảm ơn.
OO

5

Khi tôi chỉ định mã ajax với Jasmine, tôi giải quyết vấn đề bằng cách theo dõi bất kỳ hàm phụ thuộc nào khởi tạo cuộc gọi từ xa (chẳng hạn như $ .get hoặc $ ajax). Sau đó, tôi truy xuất các lệnh gọi lại được đặt trên đó và kiểm tra chúng một cách riêng lẻ.

Đây là một ví dụ mà tôi đã giới thiệu gần đây:

https://gist.github.com/946704


0

Hãy thử jqueryspy.com Nó cung cấp một cú pháp giống như jquery thanh lịch để mô tả các thử nghiệm của bạn và cho phép các cuộc gọi lại để kiểm tra sau khi ajax hoàn tất. Nó tuyệt vời để kiểm tra tích hợp và bạn có thể định cấu hình thời gian chờ ajax tối đa tính bằng giây hoặc milesec giây.


0

Tôi cảm thấy mình cần đưa ra câu trả lời cập nhật hơn vì Jasmine hiện đã ở phiên bản 2.4 và một số chức năng đã thay đổi so với phiên bản 2.0.

Vì vậy, để xác minh rằng một hàm gọi lại đã được gọi trong yêu cầu AJAX của bạn, bạn cần tạo một gián điệp, thêm một hàm callFake vào nó sau đó sử dụng gián điệp làm hàm gọi lại của bạn. Đây là cách nó diễn ra:

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

Tôi đã thực hiện thủ thuật cho hàm thành công cũng như hàm lỗi để đảm bảo rằng Jasmine sẽ chạy các thông số kỹ thuật càng sớm càng tốt ngay cả khi AJAX của bạn trả về lỗi.

Nếu bạn không chỉ định một hàm lỗi và AJAX của bạn trả về lỗi, bạn sẽ phải đợi 5 giây (khoảng thời gian chờ mặc định) cho đến khi Jasmine thông báo lỗi Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.. Bạn cũng có thể chỉ định thời gian chờ của riêng mình như sau:

it("should get the content of an XML file", function(done)
{
    // your code
},
10000); // 10 seconds
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.