Fri 23 Sep 2011
Easy mocking in your JavaScript tests
At Rally, we write a lot of JavaScript. That means we also write a lot of JavaScript tests. In this post, we’ll talk about some techniques we are using to mock out dependencies in our JavaScript tests.
Some History
We’ve always had a need to mock out dependencies in our JavaScript tests, but the way we’ve accomplished it has varied widely. At first, we used simple objects and variables:
'test should switch projects' : function() {
var actualProjectOid = null;
var projectSwitcher = {
switchTo : function(oid) {
actualProjectOid = oid;
}
};
var projectPicker = new Rally.app.ui.ProjectPicker({
projectSwitcher : projectSwitcher
});
var projectOid = 123;
projectPicker.fireEvent('projectselect', projectOid);
assertEquals(
'Should have switched project using the oid provided by event',
projectOid,
actualProjectOid);
}
This approach is simple and it works, but it leads to lots of extra variables and function definitions. It also doesn’t read very well.
Our next attempt was to use a built-in feature of JsTestDriver – expectAsserts.
'test should switch projects' : function() {
expectAsserts(1);
var projectOid = 123;
var projectSwitcher = {
switchTo : function(oid) {
assertEquals(
'Should have switched project using the oid provided by event',
projectOid,
oid);
}
};
var projectPicker = new Rally.app.ui.ProjectPicker({
projectSwitcher : projectSwitcher
});
projectPicker.fireEvent('projectselect', projectOid);
}
The expectAsserts will ensure the provided number of asserts were called during the execution of the test case. If the expected number of asserts were not called, the test will fail. There biggest thing we didn’t like about this approach is that if the asserts aren’t called, the error message provided isn’t very helpful. We’d really like to get the message provided to the Assert.areEqual if the test fails. It also doesn’t read as nicely as we’d like.
In Comes Sinon.js
Sinon.js written by Christian Johansen is a great mocking framework for JavaScript testing. It provides the ability to spy, stub, mock, and verify in your tests. The documentation is great, and there are features for just about any testing situation you might find yourself in.
'test should switch projects' : function() {
var projectSwitcher = {
switchTo : sinon.stub()
};
var projectPicker = new Rally.app.ui.ProjectPicker({
projectSwitcher : projectSwitcher
});
var projectOid = 123;
projectPicker.fireEvent('projectselect', projectOid);
sinon.assert.calledWith(projectSwitcher.switchTo, projectOid);
}
There are a few things to note about this approach. It leads to readable code – the assert reads like a sentence. It’s the smallest method in terms of lines of code that we’ve shown. We aren’t creating a lot of unnecessary variables to track execution of methods, and we will get meaningful error messages if the test fails.
