Unit Testing Self-Invoking JavaScript Functions
Using QUnit and Sinon.js achieves the possibility of unit testing — and using the Google closure compiler with hard optimization enabled minifies code efficiently.
Join the DZone community and get the full member experience.
Join For FreeWhen Akanoo started out, we only tested our components run on Java — either written in Scala or using Grails or Groovy — with JUnit, ScalaTest, and Spock. By the end of 2015, we also wanted unit tests for our JavaScript tracking library that is integrated in our client’s online shops.
Finding the Right Test Framework
Nobody in our team had experience in testing JavaScript, so I went out looking for JavaScript unit test frameworks. What does one do if one has no clue? Google, of course. The first hit was QUnit, developed and used by the jQuery team. I looked into other libraries but decided to give QUnit a shot. The reasons I chose it were:
- Support by the community (in this case, the jQuery community).
- Used by some big players (jQuery certainly is a big player.)
- Easy to use (check out the cookbook).
- Easy to understand for Java developers (hence, similarity with JUnit).
- Plug-in support and availability of plug-ins (we’ll come to this later).
So, I had chosen my test framework. Let’s write some tests.
Testing Self-Invoking JavaScript Functions
Well, testing wasn’t that easy at the beginning. Our tracking script is encapsulated in a self-invoking function also known as immediately-invoked function expression (IIFE). The reason behind this is to avoid polluting the global space (everything in the window object) with our functions and variables and only allow access via our API calls using the exposed function at()
. See an example of an immediately-invoked function expression below:
<script>
(function(a, b) {
// our tracking code goes here
var foo = "bar";
function bla() {
// sample function
console.log(foo);
}
})(window, document);
</script>
As the name says, the function is immediately invoked after definition and I have no chance to call the function itself or to access the variables and functions inside. What could I do to test the function bla()
in the above example?
My first idea was to comment out the two lines that define the function as self-invoking and define the parameterized variables by hand:
<script>
var a = window,
b = document; //(function(a, b) {
// our tracking code goes here
var foo = "bar";
function bla() {
// sample function
console.log(foo);
}
//})(window, document);
</script>
Spying With Sinon.js
I finished writing some easy test checking for the output of functions with various input parameters. Unfortunately, some functions are called inside other functions — and these make for harder testing. If I want to know whether the inner function has been called, I need to spy on its execution.
<script>
function A() {
B();
}
function B() {
// do something
}
A();
</script>
QUnit itself doesn’t offer functions for spying, so I googled again to find a solution for JavaScript mocking. I stumbled upon Sinon.js, which offers spies and stubs and integrates nicely with QUnit.
I started to write some tests, but the function sinon.spy
(object, “function”) requires us to specify the encapsulating object of the “function” to spy. After my changes to the self-invoking function expression, the functions reside in the “global” scope, which, in JavaScript, means that they lie under the window object.
// the following line will be replaced by the code in the comment
var a = window,
b = document; //(function(a, b) {
// our tracking code goes here
var obj = {
// variables go here
foo: "bar";
}
// functions go like this
obj.bla = function() {
// sample function
console.log(foo);
}
//})(window, document);
Unfortunately, I wasn’t able to spy functions using Sinon on the window object. So, I went on putting all functions inside my faked IIFE inside an object to make them testable. Of course, I had to refactor the existing code already to some extent, i.e., extracting functions, avoiding anonymous functions, etc. I strongly recommend using JSHint.
Notice: When asserting spies with Sinon’s function with Args (arg0
, arg1
, and so on), be aware that the order of arguments must be the same as in the call. If you want to check for the first two of three arguments, you may omit the third but if you want to check for the latter ones, the first argument needs to be defined.
Stubbing With Sinon.js
Sometimes, the result of function A is dependent on the result of function B that is called inside function A. To test, I have to manipulate the inner function B to always return a predefined value. This is called stubbing and can be done with Sinon.js by calling sinon.stub(object, “function”). You should also check out the faked XHR and faked server that Sinon offers. They were very helpful for my unit tests.
Conclusion
If you’ve read until now, you certainly can guess that it was a long way down the road to 95% test coverage (measured with JSCover). The upsides of this solution using QUnit and Sinon.js is apparently that we achieved the possibility of unit testing. The obvious downside is the superfluous object in the IIFE. That is, however, not so bad, as the Google closure compiler with hard optimization enabled minifies the code efficiently.
Published at DZone with permission of Bendix Saeltz. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments