As developers, we know how important testing our code is and furthermore how much we can gain from automating these tests. That said, most modern web systems are inherently difficult to test due to their interconnected nature. Depending on the architecture of your application it’s at best tedious to test just your code and at worst impossible.
Why is interconnectivity a problem you might ask? Consider an API which pulls information from third party sources. You really only need to test that your code is behaving correctly; the third party sources are out of your control. By creating a system that relies on external data sources you have created a dependency on testing those sources – in order to confirm that your code behaves correctly it needs to send and receive data from the third party source. This makes unit testing nearly impossible and functional testing slow at best and unreliable at worst.
It doesn’t have to be this way though. One of the oft-maligned but actually quite useful features of JavaScript is its prototypal object oriented design. It’s confusing at first if you have little experience with object oriented programming to begin with or are more familiar with classical object oriented programming, but it holds the key to solving our problem of external dependencies when testing.
It’s best to start by thinking of (almost) everything in JavaScript as an object – from strings to functions and everything in between. When you grasp that it’s not a huge jump to understand that you could override properties of that object. Consider the Date object in JavaScript. By default its getTime()
method will return a unix timestamp in milliseconds:
But we can easily alter this behavior by overriding the getTime()
method of the Date prototype object:
You’ll notice that we were able to change the behavior of the already defined object’s getTime()
method by altering the Date prototype. “This is great, now I can change everything!” You might say, followed immediately by the realization of “but wait, ain’t nobody got time for that!” Fear not fellow developer, in typical open source fashion, there’s a library for that. Before we dig into that though, it should be noted that altering fundamental prototypes can be risky but is extremely powerful, especially when it comes to testing when we want to change fundamental behavior of core JavaScript methods.
sinon.js provides “[s]tandalone test spies, stubs and mocks for JavaScript.” It has no dependencies and will work with any unit testing framework. In short, they’ve done a lot of the work for you in writing mock servers, timers, events, and providing a framework to override the base behavior of external libraries.
So let’s look at an example. Say we have some code that fetches a list of posts from an external API:
var pollForPosts = function () { var doSomethingCool = function (data) { // Contrived example of doing something cool with our data. console.log(JSON.parse(data)); } jQuery.ajax('http://example.com', doSomethingCool); };
During testing, we don’t want the overhead of waiting for HTTP requests to make sure that our code behaves correctly. Furthermore, we don’t want a failure in HTTP transport or the external service to cause false positives in our test framework. Luckily, with sinon we can easily override the behavior of jQuery.ajax
by defining a stub in our test framework:
sinon.stub(jQuery, 'ajax', function (url, cb) { if (url === 'http://example.com') { cb(JSON.stringify({ posts: [ { title: 'Vegan pharetra pellentesque' }, { title: 'Foodie eros maecenas' } ] })); } }); pollForPosts();
Now when our code is executed by the test framework the stub we defined will be called instead of jQuery.ajax
and our example data will be returned. We’ve removed the reliance on HTTP transport and external services and can instead focus on whether or not our code is actually working.
Another important factor in testing is speed. Tests that take a long time to run are unlikely to be run often and tests that aren’t run often are more difficult to maintain and less useful for catching problems. Sometimes a delay is unavoidable, right? Like this:
setInterval(pollForPosts, 1000);
Wrong! Sinon has us covered here too by completely overriding JavaScript’s timer behaviors. By telling the test framework that you’re using a fake timer, then “ticking” time you can instantaneously jump forward in time without the wait:
var clock = sinon.useFakeTimers(); clock.tick(1000);
Now we’ve removed the delay in our code. This also has the added benefit of stopping time until you tick again. In our example this means that although we are polling on an interval the code will only be run once because only 1000ms have passed on our fake clock.
I hope with sinon in your toolkit any barriers you had to testing JavaScript will start to be broken down. A clean codebase is one that’s easily testable, and a well tested codebase tends to be clean.
Making the web a better place to teach, learn, and advocate starts here...
When you subscribe to our newsletter!