Как эффективно тестировать отложенный всегда

#javascript #jquery #promise #qunit

#javascript #jquery #обещание #qunit

Вопрос:

При написании тестов для кода в jQuery ajax (или get) всегда часть или даже в bluebird обещает, наконец, вот так:

 function doStuff() {
    console.log('stuff done');
}

function someFunction() {
    return $.get('someurl').always(doStuff);
}
  

Я всегда обнаруживаю, что пишу (QUnit) тесты для этого, например:

 QUnit.test("doStuff will be called when someFunction succeeds", function (assert) {
    var deferred = $.Deferred();
    var backup = $.get;
    $.get = function () { return deferred; };

    var doStuffIsCalled = false;
    doStuff = function(){ doStuffIsCalled = true; };

    deferred.resolve({});
    return someFunction().then(function(){
        $.get = backup;
        assert.ok(doStuffIsCalled);
    });
});

QUnit.test("doStuff will be called when someFunction fails", function (assert) {
    var deferred = $.Deferred();
    var backup = $.get;
    $.get = function () { return deferred; };

    var doStuffIsCalled = false;
    doStuff = function(){ doStuffIsCalled = true; };

    deferred.reject(new Error('some error'));
    return someFunction().catch(function(){
        $.get = backup;
        assert.ok(doStuffIsCalled);
    });
});
  

Это работает, но несколько многословно. Есть ли какой-нибудь более эффективный способ, предпочтительно в одном тесте, напрямую тестировать код, вызываемый в всегда части отложенного?

Ответ №1:

Вы можете использовать Sinon.js издеваться над jQuery ajax (или get), а также над обещаниями в целом.

Одним из подходов может быть:

 function someFunction() {
    return $.get('/mytest').always(doStuff);
}

function givenFncExecutesAndServerRespondsWith(reponseNumber, contentType, response) {
    server.respondWith("GET", "/mytest", [reponseNumber, contentType, response]);
    someFunction();
    server.respond();
}

module("Testing server responses", {
    setup: function () {
        server = sinon.sandbox.useFakeServer();
        doStuff = sinon.spy();
    },
    teardown: function () {
        server.restore();
    }
});

test("doStuff will be called when someFunction succeeds", function () {
    givenFncExecutesAndServerRespondsWith(200, '', '');
    ok(doStuff.called, "spy called once");
});

test("doStuff will be called when someFunction fails", function () {
    givenFncExecutesAndServerRespondsWith(500, '', '');
    ok(doStuff.called, "spy called once");
});
  

Вы можете поиграть с этим кодом в этой скрипке. Если вместо always того, чтобы вы использовали done или fail для вызова обратного вызова, соответствующий тест завершится неудачей.

Объяснение кода будет следующим:

  1. Создайте поддельный сервер и шпиона, который будет действовать как always обратный вызов.
  2. Измените номер ответа ответа сервера в соответствии с тем, что мы тестируем.

Надеюсь, это поможет.

Комментарии:

1. Спасибо @acontell! Это определенно гораздо более компактное решение, чем мое. Было бы еще лучше, если бы это можно было сделать в одном тесте, но я думаю, что это невозможно, верно? Вам всегда нужно будет проверять путь успеха и сбоя.

2. Добро пожаловать, @PhilipBijker! С моей точки зрения, для того, чтобы тестировать всегда, было бы достаточно одного теста. Причина в том, что независимо от того, выполняется обещание или нет, результат будет одинаковым (поэтому одного теста в любом случае должно быть достаточно). Однако, если бы у нас были обратные вызовы done и fail вместо этого в someFunction, тогда потребовались бы два теста, и эта реализация была бы более подходящей.