Bagaimana cara memverifikasi peristiwa jQuery AJAX dengan Jasmine?


114

Saya mencoba menggunakan Jasmine untuk menulis beberapa spesifikasi BDD untuk permintaan AJAX jQuery dasar. Saat ini saya menggunakan Jasmine dalam mode mandiri (yaitu melalui SpecRunner.html). Saya telah mengkonfigurasi SpecRunner untuk memuat jquery dan file .js lainnya. Ada ide mengapa hal berikut ini tidak berhasil? has_returned tidak menjadi kenyataan, bahkan mengira "yuppi!" peringatan muncul dengan baik.

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

});

Bagaimana cara menguji apakah callback telah dipanggil? Petunjuk apa pun ke blog / materi yang terkait dengan pengujian async jQuery dengan Jasmine akan sangat dihargai.

Jawaban:


234

Saya kira ada dua jenis tes yang dapat Anda lakukan:

  1. Tes unit yang memalsukan permintaan AJAX (menggunakan mata-mata Jasmine), memungkinkan Anda untuk menguji semua kode Anda yang berjalan tepat sebelum permintaan AJAX, dan setelahnya . Anda bahkan dapat menggunakan Jasmine untuk memalsukan respons dari server. Pengujian ini akan lebih cepat - dan tidak perlu menangani perilaku asinkron - karena tidak ada AJAX nyata yang terjadi.
  2. Tes integrasi yang melakukan permintaan AJAX nyata. Ini harus asinkron.

Jasmine dapat membantu Anda melakukan kedua jenis tes tersebut.

Berikut adalah contoh bagaimana Anda dapat memalsukan permintaan AJAX, dan kemudian menulis pengujian unit untuk memverifikasi bahwa permintaan AJAX yang dipalsukan mengarah ke URL yang benar:

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

Untuk penggunaan Jasmine 2.0 sebagai gantinya:

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

seperti yang tercantum dalam jawaban ini

Berikut adalah pengujian unit serupa yang memverifikasi bahwa panggilan balik Anda telah dijalankan, setelah permintaan AJAX berhasil diselesaikan:

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

Untuk penggunaan Jasmine 2.0 sebagai gantinya:

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

seperti yang tercantum dalam jawaban ini

Terakhir, Anda telah mengisyaratkan di tempat lain bahwa Anda mungkin ingin menulis pengujian integrasi yang membuat permintaan AJAX nyata - untuk tujuan integrasi. Ini bisa dilakukan menggunakan fitur asinkron Jasmine: waits (), waitsFor (), dan 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 Alex jawaban bagus. Sebenarnya saya menghadapi masalah serupa yang saya telah membuka pertanyaan Test Ajax request menggunakan objek Deferred . Bisakah Anda melihatnya? Terima kasih.
Lorraine Bernard

12
Saya sangat berharap jawaban Anda menjadi bagian dari dokumentasi resmi untuk Jasmine. Jawaban yang sangat jelas untuk masalah yang telah membunuh saya selama beberapa hari.
Darren Newton

4
Jawaban ini harus benar-benar ditandai sebagai jawaban resmi untuk pertanyaan ini.
Thomas Amar

1
Cara terkini untuk mendapatkan informasi panggilan terbaru adalah $ .ajax.calls.mostRecent ()
camiblanch

1
Bagaimana Anda menerapkannya untuk ajax JS biasa?
Pembuat jam

13

Lihat proyek melati-ajax: http://github.com/pivotal/jasmine-ajax .

Ini adalah drop-in helper yang (baik untuk jQuery atau Prototype.js) bertopik di lapisan XHR sehingga permintaan tidak pernah keluar. Anda kemudian dapat mengharapkan semua yang Anda inginkan tentang permintaan tersebut.

Kemudian ini memungkinkan Anda memberikan tanggapan tetap untuk semua kasus Anda dan kemudian menulis tes untuk setiap tanggapan yang Anda inginkan: sukses, gagal, tidak sah, dll.

Ini membuat panggilan Ajax keluar dari ranah pengujian asinkron dan memberi Anda banyak fleksibilitas untuk menguji cara kerja penangan respons Anda yang sebenarnya.


Terima kasih @jasminebdd, proyek melati-ajax terlihat seperti cara untuk menguji kode js saya. Tetapi bagaimana jika saya ingin menguji dengan permintaan aktual ke server, misalnya untuk uji konektivitas / integrasi?
mnacos

2
@mnacos jasmine-ajax sebagian besar berguna untuk pengujian unit dalam hal ini Anda secara khusus ingin menghindari panggilan ke server. Jika Anda melakukan pengujian integrasi, ini mungkin bukan yang Anda inginkan dan harus dirancang sebagai strategi pengujian terpisah.
Sebastien Martin

7

berikut adalah contoh rangkaian pengujian sederhana untuk aplikasi js seperti ini

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

contoh rangkaian pengujian, yang akan memanggil callback berdasarkan regexp url

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

Terima kasih Bung. Contoh ini sangat membantu saya! Perhatikan bahwa jika Anda menggunakan Jasmine> 2.0, sintaks untuk andCallFake adalah spyOn (jQuery, "ajax"). And.callFake (/ * fungsi Anda * /);
João Paulo Motta

Tidak yakin apakah ini adalah masalah versi tetapi saya mendapat kesalahan .andCallFake, gunakan .and.callFakesebagai gantinya. Terima kasih.
OO

5

Ketika saya menentukan kode ajax dengan Jasmine, saya menyelesaikan masalah dengan memata-matai fungsi bergantung apa pun yang memulai panggilan jarak jauh (seperti, katakanlah, $ .get atau $ ajax). Kemudian saya mengambil callback yang disetel di atasnya dan mengujinya secara diam-diam.

Berikut adalah contoh yang saya dapatkan baru-baru ini:

https://gist.github.com/946704


0

Coba jqueryspy.com Ini menyediakan jquery elegan seperti sintaks untuk menggambarkan pengujian Anda dan memungkinkan panggilan balik untuk menguji setelah ajax selesai. Ini bagus untuk pengujian integrasi dan Anda dapat mengonfigurasi waktu tunggu ajax maksimum dalam hitungan detik atau milidetik.


0

Saya merasa perlu memberikan jawaban yang lebih up-to-date karena Jasmine sekarang ada di versi 2.4 dan beberapa fungsi telah berubah dari versi 2.0.

Jadi, untuk memverifikasi bahwa fungsi panggilan balik telah dipanggil dalam permintaan AJAX Anda, Anda perlu membuat mata-mata, menambahkan fungsi callFake ke sana kemudian menggunakan mata-mata sebagai fungsi panggilan balik Anda. Begini caranya:

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

Saya telah melakukan trik untuk fungsi sukses serta fungsi kesalahan untuk memastikan bahwa Jasmine akan menjalankan spesifikasi secepat mungkin bahkan jika AJAX Anda mengembalikan kesalahan.

Jika Anda tidak menentukan fungsi kesalahan dan AJAX mengembalikan kesalahan, Anda harus menunggu 5 detik (interval waktu tunggu default) sampai Jasmine membuat kesalahan Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.. Anda juga dapat menentukan waktu tunggu Anda sendiri seperti ini:

it("should get the content of an XML file", function(done)
{
    // your code
},
10000); // 10 seconds
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.