I'm far from a JavaScript expert, but I've done some web development using JavaScript before and one thing I have always wondered about was how to write automated unit tests. Of course you'll probably still want to do manual testing and functional testing with something like
Selenium. It seems like unit tests would greatly improve the development experience though. The thing is, I had no idea what a JavaScript unit test would look like, so I chose to investigate it as the subject for my first blog.
The advantages of unit testing JavaScript are the same as unit testing in other programming languages: looking for regressions is easier. It also promotes better code design because you can either refactor with confidence or are doing TDD and writing testable code from the start. And because our execution environment is the browser, unit tests make it incredibly easier to do cross-browser testing.
The basic requirements for being able to write unit tests are that your JavaScript is in separate files from your HTML and that you write granular, parameterized functions. Then you can call these functions from a test HTML file and run the tests by opening the HTML file in a browser.
I'll quickly mention that I came across
PhantomJS for running headless tests and an intriguing project called
Karma for hooking your tests into your continuous integration process. I'm sure there are dozens of other great JavaScript frameworks too. Again, for this post I want to keep it simple and just go though some baisc examples of how to get started writing unit tests.
Getting Started With The Code
This is the test HTML file. Originally I wanted to do this with pure JavaScript, but it quickly became apparent that it would be easier to use an existing testing framework and not try to reinvent the wheel. I chose
QUnit. Many of their examples use
jQuery so I will pull that in too. It also seems like the subject of mocking would come up pretty quickly testing a site with AJAX calls and I would like to see how that work. I will use
jQuery Mockjax for a mocking example. The purpose of this post is not to learn about or endorse any specific frameworks though. The examples below should be understandable without having used any of the frameworks before. I chose these three because they are light-weight and seem to be easy to get started with.
I include the js files for jQuery, QUnit, Mockjax (and json2 which it needs), and testable_js (my JavaScript code that I am trying to test). QUnit also needs a CSS file for displaying the results. Your JavaScript should only rely on the #qunit-fixture element. QUnit will reset elements inside the #qunit-fixture element after each test so that all tests can be atomic.
The JavaScript that I am testing is largely based on the
QUnit Cookbook and this
tutorial. I have some basic functions, a function that makes a HTTP request to Flickr, a function that makes a HTTP request to a not-yet-implemented page, a couple functions that modify the DOM, and a key logger function.
To test these I need to be able to test synchronous and asynchronous callbacks, simulate user actions (like pressing keys), the tests need to be atomic (since the DOM will be modified more than one), and I need to be able to mock an HTTP request (to the not-yet-implemented page).
Adding Actual Tests
Here I define a module with two tests. A module is just a logical grouping. Each test modifies the DOM and I check that only one change was made, so as I mentioned earlier they have to be atomic. This is also why suggested that parameterized functions are necessary.
Here I test my key logger by simulating a user pressing keys. This is obviously a contrived example, but I thought it was pretty cool nonetheless and is what convinced me to use jQuery.
Here I've mocked an AJAX call and have it return an error status code. The test is that my error handling callback is called. The test would fail after half a second if there was no response.
Here I've again mocked an AJAX call and have it return a success status code. The test is that my success handling callback is called. This is pretty cool and seemingly very useful too. You can set the response time, response body, headers, etc. too. Someone can then work on the front-end of a site before the back-end is finished.
Results
Look
here to see my complete test HTML file. I included a few other tests and show how to do setup/teardown for a module which is another nice QUnit feature. Once finished when I open tests.html in a web browser this is the report I see:
It's still a big leap to go from these simple examples to testing a real site, but I learned that the barrier to entry for writing JavaScript unit tests is lower than expected and you can get going in a couple hours with existing opensource tools. I think I would try to include something like this in a future JavaScript project.