Selecting a unit test framework for a JavaScript-only application - not a task with an obvious answer! Coming from the Rails world, where most people work with either TestUnit or RSpec, I was a bit lost when I started researching. This table on wikipedia was quite helpful for a first overview. But: JsUnit, JSTest, jsUnitTest, jsUnity - these are actually different frameworks, and there’s even many more …
I narrowed the selection down to those being currently developed, or are in use in “big” projects. JsUnit seems to be one of the major players, but I found almost no documentation, and the code base hasn’t changed a lot in the last years. That’s not a negative thing in itself, but having an active community around is a good thing. QUnit is used for testing JQuery, it looked like you could work with it as well, but its syntax and setup didn’t make me go “wow” exactly. Same goes for JQUnit, about which I had read good stuff in several blog posts. It’s also what Sammy.js is tested with.
I like RSpec, so when I had my first look at ScrewUnit, I was delighted to find there’s BDD syntax for JavaScript too. ScrewUnit is also a part of Blue Ridge, which is sadly still only available as a Rails plugin. So, ScrewUnit looked nice.
And THEN … I found JSpec. I was thrilled! It’s a JavaScript framework, but it doesn’t look like JavaScript at all: no curly brackets, no semicolons, just plain domain language. It just looks like RSpec. Here is a random snippet from my test suite, just have a look:
describe 'NoteCollection'
before_each
note3 = new Note({id: "8c8", text: 'three'});
note1 = new Note({id: "ae9", text: 'one', next_id: "107"});
note2 = new Note({id: "107", text: 'two', next_id: "8c8"});
notes = new NoteCollection([note3, note1, note2]);
end
describe 'firstNote'
it 'should return a note'
notes.firstNote().should.be_an_instance_of Note
end
it 'should return the first note'
notes.firstNote().id.should.eql note1.id
end
it 'should throw an error if there is more than one note that could be the first note'
note4 = new Note({id: "fff", rev: "2-420"});
notes.notes.push(note4);
-{notes.firstNote()}.should.throw_error 'More than one first note found'
end
end
endAnd that are just 3 out of over 45 core matchers. There are also matchers for JQuery, see the next example, where I put the DOM I wanted to test in a fixture called outline.html. In Rails I don’t use fixtures, but if you want to test an application where all the markup is generated out of many different sources, it makes sense to test little pieces of the DOM in isolation. In ScrewUnit you have only one file for your all your mocked DOM.
describe 'NoteElement'
before_each
outline = elements(fixture('outline'))
notes = outline.find('li')
parent_note = new NoteElement($(notes.get(0)))
child_note = new NoteElement($(notes.get(1)))
end
describe 'focusNextTextarea'
it 'should focus the first child note if there is one'
parent_note.focusNextTextarea();
parent_note.noteLi().should.not.have_attr("data-focus")
child_note.noteLi().attr("data-focus").should.eql 'true'
end
end
describe 'insertUpdateNotePointers'
it 'should call setNextPointerToNewlyInsertedNote'
inserted_note = {id:'4', text: 'inserted!'};
parent_note.should.receive('setNextPointerToNewlyInsertedNote').with_args(inserted_note)
parent_note.insertUpdateNotePointers(inserted_note);
end
end
endNote, if you don’t like the RSpec-like Syntax, you can also skip the JSpec DSL and just use plain Javascript syntax.
More JSpec goodness: You can either run the tests in the browser, or in the terminal, so you can include it in your CI. In the terminal you can specify the browsers it should be tested with: The browsers will be opened in the background, but you won’t see your stuff being run like in Selenium. You can mock timers to test asynchronous calls. You can stub methods, mock AJAX requests, specify shared behaviours, hook into JSpec to specify your own matchers or do whatever else, and … just see for yourself.
Installation is easy too: just add JSpec.js and JSpec.css to your application. If you need them, add the files for jquery, timers or xhr support too. In spec/index.html you include your application files and specify which tests you want to run. Then open it in the browser, wait a few milliseconds, and see the result. If you want to run JSpec tests in the terminal, just install the JSpec Ruby gem. For this you also have to install Rhino (a Java-based JavaScript interpreter) - when you are on OS X, this should get you started.
What I don’t like about JSpec … the BlueRidge browser output is much more clear and pretty. I thought really hard about more things but that’s it.
Make sure to read the documentation on the JSpec website as well as the github Readme. Both ressources look similar, but at the time of writing there were different things on the two pages.
Trackbacks
Use the following link to trackback from your own site:
http://lenaherrmann.net/trackbacks?article_id=9
-
This post was mentioned on Twitter by kilaulena: I wrote a blog post about my favorite JavaScript Unit Testing framework: http://bit.ly/6CATqS #jspec
1 day later:
Thanks for sharing this ! Haven’t gotten round to testing my js yet (bad, I know).
1 day later:
great write-up Lena!
Seems like there are no excuses left for me now to not write any JS app with BDD :)
3 months later:
Thanks for the overview - very informative. Jspec looks very cool indeed!
3 months later:
Nice article Lena, I’ve found myself writing way more JS lately, and I’m hoping JSpec will help me sleep better at night…
The Rhino integration is great, makes it super easy to add JSpec into my ci workflow.