ฉันจะตรวจสอบเหตุการณ์ jQuery AJAX กับ Jasmine ได้อย่างไร


114

ฉันพยายามใช้ Jasmine เพื่อเขียนข้อกำหนด BDD สำหรับคำขอ jQuery AJAX พื้นฐาน ฉันกำลังใช้ 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);
  }); 

});

ฉันจะทดสอบได้อย่างไรว่ามีการโทรกลับแล้ว คำแนะนำใด ๆ สำหรับบล็อก / เนื้อหาที่เกี่ยวข้องกับการทดสอบ async jQuery กับ Jasmine จะได้รับการชื่นชมอย่างมาก

คำตอบ:


234

ฉันเดาว่ามีการทดสอบสองประเภทที่คุณสามารถทำได้:

  1. การทดสอบหน่วยที่ปลอมคำขอ AJAX (โดยใช้สายลับจัสมิน) ช่วยให้คุณสามารถทดสอบทั้งหมดของรหัสของคุณที่วิ่งก่อนคำขอ 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 จริง - เพื่อวัตถุประสงค์ในการรวม สามารถทำได้โดยใช้คุณสมบัติที่ไม่ตรงกันของจัสมิน: รอ (), รอ () และรัน ():

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 ที่จริงผมต้องเผชิญกับปัญหาที่คล้ายกันซึ่งผมได้เปิดคำถามคำขอทดสอบอาแจ็กซ์ใช้วัตถุรอการตัดบัญชี ลองดูหน่อยได้ไหม ขอบคุณ
Lorraine Bernard

12
ฉันหวังว่าคำตอบของคุณจะเป็นส่วนหนึ่งของเอกสารอย่างเป็นทางการสำหรับจัสมิน คำตอบที่ชัดเจนมากสำหรับปัญหาที่คร่าชีวิตฉันมาสองสามวัน
Darren Newton

4
คำตอบนี้ควรถูกทำเครื่องหมายว่าเป็นคำตอบอย่างเป็นทางการสำหรับคำถามนี้
Thomas Amar

1
วิธีรับข้อมูลการโทรล่าสุดคือ $ .ajax.calls.mostRecent ()
camiblanch

1
คุณจะนำไปใช้กับ JS ajax ธรรมดาได้อย่างไร
ช่างซ่อมนาฬิกา

13

ดูที่โครงการมะลิอาแจ็กซ์: http://github.com/pivotal/jasmine-ajax

เป็นตัวช่วยแบบดรอปอินที่ (สำหรับ jQuery หรือ Prototype.js) ที่ชั้น XHR เพื่อไม่ให้คำขอออกไป จากนั้นคุณสามารถคาดหวังสิ่งที่คุณต้องการเกี่ยวกับคำขอ

จากนั้นจะช่วยให้คุณสามารถให้คำตอบสำหรับทุกกรณีของคุณจากนั้นเขียนการทดสอบสำหรับแต่ละคำตอบที่คุณต้องการ: ความสำเร็จความล้มเหลวไม่ได้รับอนุญาต ฯลฯ

Ajax ใช้เวลาในการเรียกออกจากขอบเขตของการทดสอบแบบอะซิงโครนัสและให้ความยืดหยุ่นอย่างมากสำหรับการทดสอบว่าตัวจัดการการตอบสนองจริงของคุณควรทำงานอย่างไร


ขอบคุณ @jasminebdd โครงการจัสมินอาแจ็กซ์ดูเหมือนจะเป็นวิธีทดสอบโค้ด js ของฉัน แต่ถ้าฉันต้องการทดสอบด้วยคำขอจริงไปยังเซิร์ฟเวอร์เช่นการทดสอบการเชื่อมต่อ / การรวมระบบล่ะ
mnacos

2
@mnacos jasmine-ajax ส่วนใหญ่มีประโยชน์สำหรับการทดสอบหน่วยซึ่งในกรณีนี้คุณต้องการหลีกเลี่ยงการเรียกไปยังเซิร์ฟเวอร์โดยเฉพาะ หากคุณกำลังทำการทดสอบบูรณาการนี่อาจไม่ใช่สิ่งที่คุณต้องการและควรออกแบบเป็นกลยุทธ์การทดสอบแยกต่างหาก
Sebastien Martin

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 (/ * your function * /);
João Paulo Motta

ไม่แน่ใจว่านี่เป็นปัญหาของเวอร์ชัน แต่ฉันพบข้อผิดพลาด.andCallFakeใช้.and.callFakeแทน ขอบคุณ
OO

5

เมื่อฉันระบุรหัส ajax กับจัสมินฉันจะแก้ปัญหาโดยการสอดแนมฟังก์ชั่นที่ขึ้นอยู่กับสิ่งที่เริ่มต้นการโทรระยะไกล (เช่นพูด $ .get หรือ $ ajax) จากนั้นฉันเรียกคืนการโทรกลับที่ตั้งไว้และทดสอบอย่างไม่น่าเชื่อ

นี่คือตัวอย่างที่ฉันให้ความช่วยเหลือเมื่อเร็ว ๆ นี้:

https://gist.github.com/946704


0

ลองใช้ jqueryspy.com มันมี jquery ที่สวยงามเช่นไวยากรณ์เพื่ออธิบายการทดสอบของคุณและอนุญาตให้เรียกกลับเพื่อทดสอบหลังจาก ajax เสร็จสมบูรณ์ เหมาะสำหรับการทดสอบการรวมและคุณสามารถกำหนดค่าเวลารอ ajax สูงสุดเป็นวินาทีหรือมิลลิวินาที


0

ฉันรู้สึกว่าต้องให้คำตอบที่ทันสมัยกว่านี้เนื่องจากตอนนี้จัสมินอยู่ที่เวอร์ชัน 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 ของคุณจะส่งคืนข้อผิดพลาดก็ตาม

ถ้าคุณไม่ได้ระบุการทำงานข้อผิดพลาดและ AJAX ของคุณกลับข้อผิดพลาดคุณจะต้องรอ 5 วินาที (ค่าเริ่มต้นหมดเวลาช่วงเวลา) 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.