Как проверить события JQuery AJAX с Jasmine?

Я пытаюсь использовать 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, будут с благодарностью.

Ответ 1

Я думаю, есть два типа тестов, которые вы можете сделать:

  • Модульные тесты, которые подделывают запрос AJAX (с использованием Jasmine spies), позволяя вам протестировать весь ваш код, который выполняется непосредственно перед запросом AJAX, и сразу после этого. Вы даже можете использовать Jasmine для подделки ответа с сервера. Эти тесты будут быстрее - и им не нужно будет обрабатывать асинхронное поведение, так как не происходит никакого реального AJAX.
  • Интеграционные тесты, которые выполняют реальные запросы AJAX. Они должны быть асинхронными.

Жасмин может помочь вам выполнить оба вида тестов.

Вот пример того, как вы можете подделать запрос AJAX, а затем напишите unit test, чтобы убедиться, что фальшивый запрос 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"
    });
}

Вместо Жасмин 2.0:

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

как указано в этом ответе

Вот аналогичный unit test, который проверяет ваш обратный вызов, после успешного завершения запроса 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
    });
}

Вместо Жасмин 2.0:

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

как указано в этом ответе

Наконец, в другом месте вы намекнули, что можете захотеть написать интеграционные тесты, которые делают реальные запросы AJAX - для целей интеграции. Это можно сделать, используя асинхронные функции Jasmine: waitits(), waititsFor() и run():

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

Ответ 2

Посмотрите на проект jasmine-ajax: http://github.com/pivotal/jasmine-ajax.

Это вспомогательный помощник, который (для jQuery или Prototype.js) заглушает на уровне XHR, чтобы запросы никогда не выходили. Затем вы можете ожидать все, что хотите от запроса.

Затем он позволяет вам предоставлять ответы на устройства для всех ваших случаев, а затем записывать тесты для каждого ответа, который вы хотите: успех, сбой, несанкционированный доступ и т.д.

Вызывает Ajax-вызовы из области асинхронных тестов и предоставляет вам большую гибкость для тестирования того, как ваши фактические обработчики ответов должны работать.

Ответ 3

вот простой пример набора тестов для приложения js, подобного этому

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

образец тестового набора, который будет вызывать обратный вызов на основе регулярного выражения 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();
 });
});

Ответ 4

Когда я указываю ajax-код с Jasmine, я решаю проблему, следя за тем, какая зависимая функция инициирует удаленный вызов (например, $.get или $ajax). Затем я извлекаю вызовы, установленные на нем, и проверяю их дискретно.

Вот пример, который я недавно использовал:

https://gist.github.com/946704

Ответ 5

Попробуйте jqueryspy.com Он предоставляет элегантный jquery, такой как синтаксис, чтобы описать ваши тесты и позволяет проверять обратные вызовы после завершения ajax. Он отлично подходит для тестирования интеграции, и вы можете настроить максимальное время ожидания ajax в секундах или миллисекундах.

Ответ 6

Мне кажется, что мне нужно предоставить более современный ответ, так как 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
        });
    });
});

Я сделал трюк для функции успеха, а также функцию ошибки, чтобы убедиться, что Jasmine будет запускать спецификации как можно скорее, даже если ваш 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