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:
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
};
}());
//Mock ajax function $. ajax = function (param) { _mockAjaxOptions = param; var fakeAjaxSuccess = { responseText: "success", textStatus: "success", XMLHttpRequest: "success" }; return fakeAjaxSuccess; };
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.
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.
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");
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With