Unit Testing JavaScript with QUnit

iseven_test

For a recent project I had to dig into my JavaScript past and work on some cross platform, enterprise level client side coding. Luckily I still remember most things, but it’s been interesting seeing the changes in the platform since I last used it extensively a couple of years ago. First step was digging up the old JavaScript unit testing frameworks I’d used, such as jsUnit. Then I remembered what a giant pain in the but jsUnit is to get working. Too many path issues, dependencies, etc.

My requirements for a testing framework are:

  • easy to setup
  • dependency free
  • simple xUnit style tests and assertions
  • runs in most browsers
  • allows my tests to be in their own js files

I was happy to discover that in my absence a very nice little testing framework has evolved. It’s called QUnit, and is used by the jquery folks for unit testing their code and is available at the QUnit GitHub Repository. It’s only got two files as dependencies, and you can load those off of the web if you’d like. It has an xUnit syntax, can work with or without the DOM, and has a super simple async testing strategy.

The test runner is basically a simple HTML page (you can make your own, or just copy their example), to which you simply include your tests as javascript src files. The API is well documented, but here’s some example tests from some code I’m testing to parse google search terms out of the referrer URL of a page.

For example,:

test("google: single word", function() {
    var r = util.getSearchEngineKeywords("http://www.google.com/search?hl=en&source=hp&biw=1082&bih=1245&q=boone&aq=f&aqi=g10&aql=&oq=");
    equal(r.searchEngine, "google", "verify search engine");
    deepEqual(r.terms, ["boone"], "verify terms");
});

test("google: multiple words", function() {
    var r = util.getSearchEngineKeywords("http://www.google.com/search?hl=en&biw=1082&bih=1245&q=Daniel+Boone&aq=f&aqi=g-s1g-sx9&aql=&oq=");
    equal(r.searchEngine, "google", "verify search engine");
    deepEqual(r.terms, ["Daniel", "Boone"], "verify terms");
});

test("google: quoted words", function() {
    var r = util.getSearchEngineKeywords("http://www.google.com/search?hl=en&biw=1082&bih=1245&q=%22Daniel+Boone%22+tracker&aq=f&aqi=&aql=&oq=");
    equal(r.searchEngine, "google", "verify search engine");
    deepEqual(r.terms, ["Daniel Boone", "tracker"], "verify terms");
});

Async tests are simple too. You just create an asyncTest, which pauses the test runner, set some timeouts, then inform the test runner when to start back up after you completed your assertions. For example, here’s a test from a session library I’m writing that verifies that 5 seconds later asking for a session gives you the same session id back.

asyncTest("default session timeout is long lived", function() {
    var session = new Session({ reset:true });
    setTimeout(function() {
        var newSession = new Session();
        equal(newSession.visitorId, session.visitorId, "visitor ids should match");
        equal(newSession.sessionId, session.sessionId, "session ids should match");
        start();
    }, 5000);
});

Here’s a link to a great QUnit tutorial, rather than writing something more detailed up myself.

Slow Motion iPhone/iPad Simulator

If you are trying to develop a particular complex animation, use this trick to turn on “slow motion mode” in the simulator. It’ll be much easier to see what’s going on.

Start the simulator as normal. next, make sure you enable “simulate hardware keyboard” in the simulator’s hardware menu, and then press your Mac’s shift key 3 times in rapid succession to toggle it on/off. You’ll see in the debugger that slow motion mode has been activated. Press the shift key 3 more times to toggle it off.

How to backup remote disks with Time Machine

I quite like Time Machine, the backup solution built into Mac OSX. I also like remote network drives like iDisk or Amazon S3 solutions. Unlike DropBox however (another great service), these drives don’t necessarily store copies of their files locally, and are therefore not backed up. In the case of iDisk, the sparsebundle image IS backed up, but that’s not really what I want. I want file level access to previous revisions of files.

So, I’ve set up a cron job which runs every hour and pulls down any updates. Very simple:

rsync -a --delete --update --whole-file /Volumes/iDisk/ /Users/duane/Backups/iDisk/