Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test this JavaScript function, including mocking of Ajax call?

As seen in this jsFiddle, I have a function 'init' that configures a button so that when clicked it opens a progress dialog and issues an Ajax call. I want to unit test this JavaScript code (using QUnit), and check the following cases:

  1. Ajax call succeeds
  2. Ajax call fails

I need to mock out at least the Ajax call and the call to window.open, and other calls I'm sure, depending on unit test implementation.

How can I write QUnit unit tests for my code that tests these two scenarios?

EDIT: The code that needs to be tested:

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
    };
}());
like image 848
aknuds1 Avatar asked Aug 22 '12 14:08

aknuds1


People also ask

How do you respond to mock Ajax?

//Mock ajax function $. ajax = function (param) { _mockAjaxOptions = param; var fakeAjaxSuccess = { responseText: "success", textStatus: "success", XMLHttpRequest: "success" }; return fakeAjaxSuccess; };

What is mocking in unit testing JavaScript?

What are Mocks? In unit testing, mocks provide us with the capability to stub the functionality provided by a dependency and a means to observe how our code interacts with the dependency.

Can we write unit test for JavaScript?

JavaScript Unit Testing is a method where JavaScript test code is written for a web page or web application module. It is then combined with HTML as an inline event handler and executed in the browser to test if all functionalities are working as desired. These unit tests are then organized in the test suite.


1 Answers

I have successfully written a QUnit test for the success case and another for the failure case, as you can see from this jsFiddle. I employed Mockjax to fake Ajax responses and simulate success/failure conditions. Notably, I configured Ajax calls to be synchronous, so that I could write synchronous tests, as I had trouble figuring out how to run my tests after asynchronous Ajax callbacks had fired.

I also make use of the Sinon.JS library to fake dependencies and verify that e.g. dialogs are launched correctly.

The working test code is included below, see my question for the function under test (statusmod.init). Let me know if there's something you think I've left out.

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");
});
like image 95
aknuds1 Avatar answered Oct 28 '22 17:10

aknuds1