Karma Test Runner with QUnit in 10 Minutes

This is a ten minute primer for using Karma and QUnit to unit test JavaScript.

Getting ready

Install Karma[1], plugins, and Qunit[2].

# Install Karma
npm install karma --save-dev
# Install plugins
npm install karma-qunit karma-phantomjs-launcher --save-dev
# Add global CLI
npm install -g karma-cli
# Install QUnit
npm install --save-dev qunitjs

How do it…

In your project directory, run the Karma initialization script:

karma init karma.conf.js

Answer the following questions[3]:

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> qunit

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> yes or no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> PhantomJS
>

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> src/js/**/*.js
> test/js/**/*.js
>

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> no

This will create the karma.conf.js configuration file for your project. To run your tests:

karma start

Put your code under the ./src/js/... directory:

src/js/core/class.js
src/js/core/input.js
src/js/core/game.js
src/js/lib/underscore-min.js
…

Put your test code under ./test/js/... directory (I mirror the src directory):

test/js/core/class.js
test/js/core/input.js
test/js/core/game.js
…

Your QUnit tests should looks something like[4]:

QUnit.test('Name of Test', function(assert) {
    // Setup the various states of the code you want to test and assert conditions.
    assert.equal(1, 1, '1 === 1');
    assert.ok(true, 'true is truthy');
    assert.ok(1, '1 is also truthy');
    assert.ok([], 'so is an empty array or object');
});

How it works…

Karma is a configurable test runner for executing any number of testing frameworks (Jasmine is really popular), but I prefer the simplicity of QUnit. It basically creates a web page, including all the files specified in the configuration, JS files followed by testing files. This runs the tests as they would be if including manually in a normal browser.

We have setup the tests to run in the PhantomJS headless browser (but you can run it against your favorite browser by updating the configuration), including all JavaScript files in the directory ./src/js/... and all testing JavaScript files in the directory ./test/js/.... Running karma start will look for a file named karma.conf.js and use it for the test runner, but you can specify another configuration with karma start other.conf.js.

Using requireJS works well with the above inclusion strategy, because the order of files doesn't matter, since each file manages its own dependencies. When not using requireJS, feel free to manually specify each file to include in karma.conf.js; they will be included in the order specified. Modifying karma.conf.js is easy and can be done any time between tests.

QUnit is a straight forward testing framework that allows you to setup conditions and assert them. Combining the two allows for quick and easy automated testing of an entire application.

When tests are setup correctly, running karma start will produce output like:

INFO [karma]: Karma v0.12.30 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket Cxgy5NBaHiEsOOJkawVE with id 16057191
PhantomJS 1.9.8 (Mac OS X): Executed 3 of 3 SUCCESS (0.001 secs / 0.006 secs)

Hopefully, this primer will help you get started testing your application. I have adopted Karma and QUnit for testing my simple Gaming Engine and already improved the code dramatically.

There’s more…

The errors are pretty obvious, but lets explore some common errors.

Import order issues or a file wasn’t included:

PhantomJS 1.9.8 (Mac OS X) ERROR
  ReferenceError: Can't find variable: _
  at /Volumes/UltraDisk/projects/html5-game-engine/src/js/core/asset.js:1

One of the tests fails an assertion:

INFO [karma]: Karma v0.12.30 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket GUH_fUDIuyUgQgO7jYE9 with id 88038210
PhantomJS 1.9.8 (Mac OS X)  Class Test FAILED
	failed, expected argument to be truthy, was: false
	Expected: true
	Actual: false
	    at /Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:1314
	    at /Volumes/UltraDisk/projects/html5-game-engine/test/js/core/classTest.js:12
	    at /Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:903
	    at /Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:1032
	    at process (/Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:591)
	    at begin (/Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:636)
	    at /Volumes/UltraDisk/projects/html5-game-engine/node_modules/qunitjs/qunit/qunit.js:652

There is an error in karma.conf.js:

INFO [karma]: Karma v0.12.30 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
WARN [watcher]: Pattern "/Volumes/UltraDisk/projects/html5-game-engine/test/js/*^&**/*.js" does not match any file.
INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket 0uCl1Pg_8cbHM90Kjmfz with id 60207047

References

  1. Karma Runner
  2. QUnit
  3. Karma Configuration
  4. QUnit Assertions

Getting Started With Node-JS

I attended the Node-JS Camp this past week and had a lot of fun talking with the developers, and familiarizing myself with the awesomeness of Node-JS. Node-JS is a powerful version of JavaScript that allows for server-side scripting and running light-weight web servers. The best part is, if you know client-side JavaScript, you know the basics of server-side JavaScript, and only need to learn how to import and use the built in libraries. Todays ...