Unit testing is something every developer agrees should be done, but often doesn’t do. Setting it up isn’t hard after reading and evaluating what feels like a 7 course meal of modules that need to come together hand in hand to allow you to write some tests. It is fairly easy after you’ve learned more than you ever wanted to know about unit testing. Not easy at all, so I’m going to distill a stack down here for you. This is just one of many possible combinations, but when you’re just trying to get started the best solution is the one you pick and stick too first.
What is in the stack?
Much of the problem with getting started is the friction of deciding which modules you want, which work well together, and did you make the right choice of technology. Don’t stress about it, any finished stack will work well. This stack is comprised of several technologies, the app framework is replaceable. For this post I will connect a JavaScript app using RequireJS to the test runner Mocha through the PhantomJS headless browser and asserted with Chai. I’m also using Backbone and MarionetteJS to illustrate frameworks in testings too.
Setup
Serving the App for Testing
In order to run testing the application will need to be hosted and accessible locally through a web browser. If you’ve already got a solution for hosting your files locally you can skip this step. For the purposes of this tutorial and testing we’ll install http-server using the command npm install http-server -g
. You can spin up a server on port 8080 now by just typing http-server app
from the test folder. The “app” parameter in this case is the folder where the server root will be. Point your browser to http://localhost:8080 and you should see a file listing for the folder you specified.
Collecting Dependencies
We’ll be using npm and Bower to bring in dependencies for this project. After getting npm installed you’ll want to install bower and the grunt-cli globally by running this command npm install bower grunt-cli -g
. You can pull down the entire working testing application from a GitHub repository I created called requirejs-unit-testing-mocha. I had started to put each file and contents in the body of this post but it got out of hand very quickly. The gist of it is that once you clone this repo you’ll need to run npm install
and bower install
from the root directory of the project.
Verify the Setup
Once you’ve cloned the repo, run npm install
and bower install
you should be able to start the http-server pointing to the app folder. Once the server is running run grunt mocha
and you should see three green tests as passed. Next we’ll go over how everything has been connected.
Wiring it up
Executing the Tests
The grunt task setup in gruntfile.js is much less complex than it may seem. The meat of the task is in the command on line 11 which is running the npm module mocha-phantomjs which loads up our tests in the PhantomJS headless browser.
Loading the Test Suite in the Browser
Loading up the all the tests is just as you’d imagine running your site would be. All the dependancies are included in the index file under our tests folder. Here we add in Mocha, Chai, all the Sinon files, and the RequireJS startup file. Important notes here are the global declaration of expect
and should
on line 37 for later usage as well as setting the RequireJS baseUrl
on line 51 which allows the included files to work as expected.
Including the Tests
The main file loaded by the test page is mochaRunner.js which includes all the tests we have for the project. The core function is what starts up the tests, either through mocha-phantomjs or just mocha if you load it in a regular browser. At this point everything needs to be ready, so the dependency list here starting on line 3 is where you include every test file you make. A tip here is to also include any other dependancies first before you list the test files out.
Writing Tests
Test file naming
Test file naming
I’ve written up one test spec file in basicTest.spec.js. Each test file follows a .spec.js extension convention. This extension isn’t required but tests files tend to be made in the same name as the JavaScript file they are testing so this extra spec tail makes them easy to differentiate. You should note right now that I haven’t followed this convention in this example test spec, which now should help you remember the right way! This file should be named sample.spec.js
. Putting the entire test suite in a subfolder this way also allows for easy exclusion during releases and deployments.
Test setup
Each set of tests starts with a describe
block taking a description parameter that helps to organize the output and group tests that have similar buildup and teardown experiences. The before
and after
functions happen before and after all the tests in the describe block are completed. Similarly the beforeEach
and afterEach
functions happen before and after each of the individual it
assertions happen. This continues for nested describe
blocks where each parent describe
block’s beforeEach
and afterEach
functions run before the current scope beforeEach
and afterEach
functions run for each it
assertion.
Assertions
Each it
block is a single assertion to test one piece of functionality, a single unit, hence the name unit tests. Each it
block expects to end with a Chai assertion. In my examples I use should.contain
and should.equals
. You can use any style of assertions, but it helps to stick to a single style in a project.
Stubbed assertion
Once you’ve included a dependacy you might need to make it respond how you want, or listen for something to have been called. This is where stubbing comes in and SinonJS starts helping out. Line 42 starts a unit test where I’ve created a stub of my show function and returned an arbitrary object instead. Note that after you stub something you should restore it as well!
Asynchronous Assertions
Not all tests can be asserted immediately but need to be asserted on a following event in the JavaScript call stack. To facilitate this you pass in a function variable to your it
function named done
. You can then call done()
explicitly in a callback or promise return to let the mocha runner know you’ve finished the test assertions.
it('async test function', function(done) {
setTimeout(function(){
done();
}, 100);
});
Where to go from here
This post covers a lot, much more than I planned. If you didn’t already have knowledge of all the tools used it might have been more daunting, but it is worth sticking to it. This is something you only have to setup a few times. Once it works you can just write tests and enjoy the benefit of them as your project and team grows. This isn’t all there is to testing but it should get you going or fill a missing gap of knowledge in getting started on testing your project.
Pingback: Unit Testing a JavaScript app - Clarify Solutions | Dovetail Software