Як я можу перевірити події jQuery AJAX за допомогою Жасмін?


114

Я намагаюся використовувати Жасмін для написання деяких специфікацій BDD для основних запитів jQuery AJAX. Зараз я використовую Жасмін в автономному режимі (тобто наскрізь 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);
  }); 

});

Як перевірити, чи був викликаний зворотний дзвінок? Будь-які вказівки до блогів / матеріалів, пов’язаних із тестуванням асинхронної jQuery з Жасмином, будуть дуже вдячні.

Відповіді:


234

Я думаю, що ви можете зробити два типи тестів:

  1. Блокові тести, які підробляють запит AJAX (використовуючи шпигуни Жасмін), дозволяють перевірити весь код, який працює безпосередньо перед запитом AJAX, і відразу після цього . Ви навіть можете використовувати Жасмін для підробки відповіді від сервера. Ці тести були б швидшими - і їм не потрібно було б поводитися з асинхронною поведінкою - оскільки немає справжнього 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 () та 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
    });
}

+1 Олексій чудова відповідь. Насправді я зіткнувся з подібною проблемою, для якої я відкрив запит Test Ajax за допомогою об’єкта Deferred . Не могли б ви поглянути? Дякую.
Лотарингія Бернарда

12
Я дуже хочу, щоб ваша відповідь була частиною офіційної документації для Жасмін. Дуже чітка відповідь на проблему, яка мене вбиває вже кілька днів.
Даррен Ньютон

4
Ця відповідь справді має бути позначена як офіційна відповідь на це питання.
Томас Амар

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 в основному корисний для тестування одиниць, в цьому випадку ви спеціально хочете уникнути дзвінка на сервер. Якщо ви робите інтеграційні тести, це, мабуть, не те, що ви хочете, і має бути розроблено як окрема стратегія тестування.
Себастьян Мартін

7

ось простий приклад тестового набору для такого додатка

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

зразок тестового набору, який буде викликати зворотний виклик на основі urge 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

Коли я вказую код ajax з Jasmine, я вирішую проблему, шпигуючи за залежною від функції, яка ініціює віддалений виклик (наприклад, $ .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
        });
    });
});

Я зробив трюк для функції успіху, а також функції помилки, щоб переконатися, що 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.