- Spawning a browser takes time and ruins my flow,
- I’m trying to drive out logic – having a browser present will lead my design to un-natural couplings, and
- It makes Continuous Integration that much harder
So I started investigating what I could do with Rhino and Envjs to make testing with Jasmine more awesome. Ingvald Skaug had been there before. It took me some time to really understand how the pieces fit together works, so I thought I’d expand on it.
Step 1: Check that Jasmine is working
I would have saved so much time if I’d started with this bit. What you need to do is download the core of Jasmine and stick it in your project. I started with the Jasmine RubyGem that spawns a browser and does the plumbing, but for this it’s back to basics. In my project it’s checked in at lib/jasmine-1.0.1. You need an HTML file to reference all the scripts and kick off the tests. Here’s an example derived from the Jasmine docs:
In my real project I generate this file at build time from an ERB template, to make sure I get all the source files and tests. However you do it, make sure it works in a browser first. Yes. Really.
Step 2: Get the bits that you need
In my lib directory I have:
- jasmine.console_reporter.js, jasmine.junit_reporter.js and envjs.bootstrap.js – all from Larry Myers’ excellent Jasmine Reporters project. Jasmine Reporters is really what glues everything together.
Step 3: Wire up Jasmine Reporters
You can have many Jasmine reporters wired up in the SpecRunner.html. In this example I’m leaving two in – the TrivialReporter that gives HTML/CSS reports, and the ConsoleReporter, which we’ll use later. Here’s the edit to the SpecRunner file now:
Step 4: Put it all together
Here’s where it all happens. In my example I use a shell script but in real life the Rakefile that generates the SpecRunner file also fires up the JVM and checks STDOUT for error messages.
#!/bin/bash java -jar lib/js.jar -opt -1 lib/envjs.bootstrap.js SpecRunner.html
envjs.bootstrap.js is worth examining, too:
This file takes the list of HTML files that you give it and tells the fake browser inside the JVM to load each one. Jasmine then fires and runs your tests:
jsimpson@curie:~/Documents/workspace/jasmine-rhino-envjs$ ./jasmine [ Envjs/1.6 (Rhino; U; Linux i386 2.6.32-26-generic; en-US; rv:1.7.0.rc2) Resig/20070309 PilotFish/1.2.13 ] Loading: SpecRunner.html Runner Started. Player : should be able to play a Song ... >> Jasmine Running Player should be able to play a Song... Passed. when song has been paused : should indicate that the song is currently paused ... >> Jasmine Running when song has been paused should indicate that the song is currently paused... Passed. when song has been paused : should be possible to resume ... >> Jasmine Running when song has been paused should be possible to resume... Passed. when song has been paused: 4 of 4 passed. Player : tells the current song if the user has made it a favorite ... >> Jasmine Running Player tells the current song if the user has made it a favorite... Passed. #resume : should throw an exception if song is already playing ... >> Jasmine Running #resume should throw an exception if song is already playing... Passed. #resume: 1 of 1 passed. Player: 8 of 8 passed. Runner Finished.
There’s also a JUnit compatible XML reporter, courtesy of Larry. This lets you make the Continuous Integration server report test results as usual.
I’m very impressed. All of my tests that used to run in the browser run headless, with some fiddling of paths. I’m using the Jasmine JQuery plugin, which probably saved my bacon on the test that is too tightly coupled to views. I’ve collected the example on GitHub.