Как выполнить модульное тестирование этой функции JavaScript, включая насмешку над вызовом Ajax?

Как видно из этого jsFiddle, у меня есть функция 'init', которая настраивает кнопку так, чтобы при нажатии она открывалась диалоговое окно прогресса и выполняет вызов Ajax. Я хочу выполнить модульное тестирование этого кода JavaScript (используя QUnit) и проверить следующие случаи:

  1. Вызов Ajax прошел успешно
  2. Вызов Ajax не работает

Мне нужно смоделировать хотя бы вызов Ajax и вызов window.open, а также другие вызовы, я уверен, в зависимости от реализации модульного теста.

Как я могу написать модульные тесты QUnit для моего кода, который проверяет эти два сценария?

EDIT: код, который необходимо протестировать:

var statusmod = (function() {
    var spinner = $("#spinner");

    var init = function(id) {
        var progressDialog = $("#progressdialog-content").dialog({
            autoOpen: false,
            title: "Launching Status Page"
        });
        var errorDialog = $("#errordialog-content").dialog({
            autoOpen: false,
            modal: true,
            buttons: {
                "OK": function() {
                    $(this).dialog("close");
                }
            }
        });

        var btn = $("#button-status");
        btn.button().click(function() {
            spinner.show();

            progressDialog.dialog("open");

            var url = $.validator.format("/api/binid/?id={0}", id);
            // Call Web service to get binary ID
            $.ajax({
                url: url,
                dataType: "json"
            }).done(function(data) {
                window.open($.validator.format("http://status/?Page=Status&Id={0}", data.Id), target = "_newtab");
            }).fail(function(jqXHR, msg, errorThrown) {
                errorDialog.dialog("open");
            }).always(function() {
                progressDialog.dialog("close");
            });

            return false;
        });
    };

    return {
        init: init,
        _spinner: spinner
    };
}());

person aknuds1    schedule 22.08.2012    source источник
comment
Рассмотрим mockjax для насмешки над вызовом ajax, работает как шарм   -  person    schedule 22.08.2012
comment
@rsplak Не могли бы вы показать, как запускать тесты после вызова обратных вызовов Ajax? Пока не вижу как.   -  person aknuds1    schedule 23.08.2012


Ответы (2)


Я успешно написал тест QUnit для случая успеха и еще один для случая отказа, как вы можете видеть из этого jsFiddle. Я использовал Mockjax для подделки ответов Ajax и имитации условий успеха/неудачи. Примечательно, что я настроил вызовы Ajax как синхронные, чтобы я мог писать синхронные тесты, поскольку у меня были проблемы с выяснением того, как запускать мои тесты после запуска асинхронных обратных вызовов Ajax.

Я также использую библиотеку Sinon.JS для имитации зависимостей и проверки того, что, например. диалоги запускаются корректно.

Рабочий тестовый код приведен ниже, см. мой вопрос для тестируемой функции (statusmod.init). Дайте мне знать, если есть что-то, что, по вашему мнению, я упустил.

var dialogSpy = null;
var windowSpy = null;
var id = "srcId";
var binId = "binId";
var url = $.validator.format("/api/binid/?id={0}", id);
var btnId = "#button-status";

module("Open status page", {
    setup: function() {
        // Allow us to run tests synchronously
        $.ajaxSetup({
            async: false
        });
        windowSpy = sinon.spy(window, "open");
        dialogSpy = sinon.spy();
        sinon.stub($.fn, "dialog", function() {
            return {
                "dialog": dialogSpy
            };
        });

        statusmod.init(id);
    },
    teardown: function() {
        windowSpy.restore();
        $.fn.dialog.restore();
        $.mockjaxClear();
        // Remove click event handler for each test
        $(btnId).unbind();
    }
});

test("Successfully open status page", function() {
    expect(4);

    $.mockjax({
        url: url,
        contentType: "text/json",
        responseText: {
            Id: binId
        }
    });

    var spinner = statusmod._spinner;
    var spinnerSpy = sinon.spy(spinner, "show");

    $(btnId).click();

    ok(spinnerSpy.calledOnce, "Spinner shown");
    ok(dialogSpy.withArgs("open").calledOnce, "Dialog opened");
    ok(dialogSpy.withArgs("close").calledOnce, "Dialog closed");
    equal(windowSpy.lastCall.args[0], $.validator.format("http://status/?Page=Status&Id={0}", binId), "Window opened");
});

test("Binary ID not found on server", function() {
    expect(3);

    $.mockjax({
        url: url,
        contentType: "text/json",
        status: 404
    });

    $(btnId).click();

    ok(dialogSpy.withArgs("open").calledTwice, "Dialogs opened");
    ok(dialogSpy.withArgs("close").calledOnce, "Progress dialog closed");
    ok(!windowSpy.called, "Window not launched");
});
person aknuds1    schedule 27.08.2012

Прежде всего, скачайте и подключите Mockjax.

Затем издевайтесь над своими вызовами ajax:

module("Mock Ajax", {
  setup: function () {
    /** this mocks the ajax call **/
    $.mockjax({
      url: 'yourUrl.php',
      data: {'action': 'your',
        'data': {
          'that': 'you',
          'send': 'here'
        }
      },
      responseText:'{"your": ["returned":"json"],"string"}'
    });
  });
});

И тогда вы можете сделать свой вызов ajax в тестовом примере:

test( "Your testcase", function() {
  $.ajax('yourUrl.php',
    data: {'action': 'your',
       'data': {
      'that': 'you',
      'send': 'here'
    }
  }, function(data) {
      ok("do something with your data");
  });
});

И вуаля, вы успешно что-то протестировали! Вы можете добавить дополнительные параметры к вызову mockjax (isTImeout, isError и т. д.). Документацию можно найти здесь.

Это основы, вы можете отредактировать их в соответствии с вашими потребностями, используя документацию, которая довольно полная.

person Community    schedule 23.08.2012
comment
Спасибо, но это не решает моей проблемы. Я не хочу делать фиктивный вызов Ajax в своем тестовом примере, я хочу вызвать status.init, нажать кнопку и убедиться, что вызов Ajax выполняется правильно и что обратные вызовы done/fail/always работают правильно. У меня есть почти рабочее решение от взлома проблемы сегодня, но в настоящее время я застрял на крошечной детали в mockjax. - person aknuds1; 23.08.2012
comment
На этой ноте, может быть, вы можете ответить на мой конкретный вопрос о mockjax, поскольку он должен быть довольно простым? stackoverflow.com/questions/12093731/ - person aknuds1; 23.08.2012