Shenandoah JavaScript Testing metapackage
Shenandoah adds support for command-line and in-browser JavaScript unit tests in your buildr, rails, or other rake-using project. It bundles several great tools together in a convention-over-configuration, Rails-like way. These tools include:
Rhino
- a Java-based JavaScript interpreterScrew.Unit
- a behaviour-driven development syntax for JavaScript similar to RSpecSmoke
- a JavaScript mocking & stubbing library similar to Mochaenv.js
- a DOM implementation written entirely in JavaScript
Shenandoah is a generalization of the Blue Ridge plugin for rails. (See below for differences.)
General Use
Shenandoah provides a command-line runner and a server to enable in-browser testing with simple path management.
The command-line runner is a lot like any other test framework's: It runs some or all of the specs and tells you which ones fail.
The server (after you start it), is available at http://localhost:4410/. The root path will give you a list of specs, linked to the pages where you can run them.
Shenandoah also provides an interactive shell a la irb
. You can use it to experiment with javascript in a similar context to the command line runner.
The following sections describe how to invoke Shenandoah with various build systems.
Installing and Running
The Shenandoah gem is hosted at Gemcutter. If you haven't yet, set up your gem environment to use Gemcutter:
$ gem install gemcutter
$ gem tumble
You can verify that this worked like so:
$ gem sources
*** CURRENT SOURCES ***
http://gemcutter.org
http://gems.rubyforge.org/
http://gems.github.com/
If the gemcutter URL isn't present, run gem tumble
again or check Gemcutter's docs for advice.
When that's square, install Shenandoah:
$ gem install shenandoah
Use with buildr
# In your buildfile
require 'shenandoah/buildr'
# ...
define :web_js do
test.using :shenandoah, :main_path => project('web')._("src/main/webapp/js")
end
In buildr, Shenandoah expects to find your specs in the src/spec/javascript
directory. The main path defaults to src/main/javascript
, but may be changed as in the example.
To run the specs from the command line:
$ buildr web_js:test
To run an individual spec file called "application_spec.js":
$ cd web_js
web_js$ buildr test:application
To start the server:
$ buildr web_js:shen:serve
To start the shell:
$ buildr web_js:shen:shell
Use with Rails
Add a config.gem entry in environment.rb:
config.gem "shenandoah", :version => '0.1.0', :lib => false
Install the rake tasks:
$ script/generate shenandoah
In a rails project, Shenandoah will look for specs in spec/javascript
, examples/javascript
, or test/javascript
. The main path is public/javascripts
.
To run the specs from the command line:
$ rake shen:spec
To run an individual spec file called "application_spec.js":
$ rake shen:spec[application]
To start the server:
$ rake shen:serve
To start the shell:
$ rake shen:shell
Use with rake (in general)
# In your Rakefile
require 'rubygems'
require 'shenandoah/tasks'
Shenandoah::Tasks.new(:main_path => 'public/javascript', :spec_path => 'test/javascript')
With plain rake, Shenandoah defaults to lib
for the main path and spec
for the spec path.
To run all the specs from the command_line:
$ rake shen:spec
To run an individual spec file called "application_spec.js":
$ rake shen:spec[application]
To start the server:
$ rake shen:serve
To start the shell:
$ rake shen:shell
Specs and Fixtures
Each Shenandoah spec is two files: a javascript spec (e.g. name_spec.js
) and and HTML fixture (e.g. name.html
). The spec contains the the actual Screw.Unit test definitions. The fixture contains the DOM that the spec (or the code under test) relies on to work.
Example Using jQuery
Shenandoah is opinionated and assumes you're using jQuery by default. The plugin itself actually uses jQuery under the covers to run Screw.Unit.
require_spec("spec_helper.js");
require_main("application.js");
Screw.Unit(function() {
describe("Your application javascript", function() {
it("does something", function() {
expect("hello").to(equal, "hello");
});
it("accesses the DOM from fixtures/application.html", function() {
expect($('.select_me').length).to(equal, 2);
});
});
});
(By the way, we don’t actually encourage you to write specs and tests for standard libraries like JQuery and Prototype. It just makes for an easy demo.)
Example Using Prototype
It's very easy to add support for Prototype. Here's an example spec:
jQuery.noConflict();
require_spec("spec_helper.js");
require_main("prototype.js");
require_main("application.js");
Screw.Unit(function() {
describe("Your application javascript", function() {
it("does something", function() {
expect("hello").to(equal, "hello");
});
it("accesses the DOM from fixtures/application.html", function() {
expect($$('.select_me').length).to(equal, 2);
});
});
});
More Examples
To see Shenandoah in action inside a working Rails app, check out the Shenandoah sample application. Among other things, this sample app includes examples of:
- using nested
describe
functions - setting up per-spec HTML "fixtures"
- stubbing functions
- mocking functions
- running the javascript specs as part of your default Rake task
JavaScript API
Shenandoah provides a handful of functions that help you write specs that can run inside a web browser as well from the Rhino command-line test runner.
require_main(filename)
Loads a dependency from the main path.
When running from the command line, require_main
becomes a Rhino call to load
, resolving the file against the configured main_path
. In a web browser, require_main
loads and evaluates the script using a synchronous background request to ensure that all the scripts are loaded in order.
require_spec(filename)
Loads a dependency from the spec path.
Just like require_main
, but for spec files.
The Shell
Shenandoah provides an irb
-like JavaScript shell for debugging your JavaScript code. jQuery and env.js are pre-loaded for you to make debugging DOM code easy.
=================================================
Rhino JavaScript Shell
To exit type 'exit', 'quit', or 'quit()'.
=================================================
- loaded env.js
- sample DOM loaded
- jQuery-1.2.6 loaded
=================================================
Rhino 1.7 release 2 PRERELEASE 2008 07 28
js> print("Hello World!")
Hello World!
js>
Note that if you have rlwrap
installed and on the command line path (and you really, really should!), then command-line history and readline arrow-up and down will be properly supported automatically. (You can get rlwrap
from your friendly neighborhood package manager.)
Mocking Example with Smoke
Smoke is a JavaScript mocking and stubbing toolkit that is somewhat similar to FlexMock or Mocha. It is automatically wired-in to undo its mocking after each Screw.Unit it
block. Here's an example.
it("calculates the total cost of a contract by adding the prices of each component", function() {
var componentX = {}, componentY = {};
mock(SalesContract).should_receive("calculateComponentPrice").with_arguments(componentX).exactly(1, "times").and_return(42);
mock(SalesContract).should_receive("calculateComponentPrice").with_arguments(componentY).exactly(1, "times").and_return(24);
expect(SalesContract.calculateTotalCost([componentX, componentY])).to(equal, 66);
});
Note that the flexible nature of the JavaScript the language means that you might not need to use Smoke, especially for stubbing.
Tips & Tricks
- Avoid using
print
in your tests while debugging. It works fine from the command line but causes lots of headaches in browser. (Just imagine a print dialog opening ten or fifteen times and then Firefox crashing. This is a mistake I've made too many times! Trust me!)
Bugs & Patches
If you see any bugs or possible improvements, please use the project's GitHub issue tracker to report them.
If you like, you could even fork the GitHub repo and start hacking.
Differences from Blue Ridge
Shenandoah is based on the Blue Ridge JavaScript Testing Rails Plugin. The main difference is that it works for any project that uses rake or buildr, not just rails projects. If you are using a rails project, you might consider using Shenandoah instead of Blue Ridge anyway:
- Blue Ridge requires all specs to be in the same directory (subdirectories are not allowed). Shenandoah does not have this limitation.
- Shenandoah's design places the spec js and the fixture html in the same directory. Blue Ridge puts them in separate directories, leading to parallel hierarchies.
- Shenandoah provides an in-browser index for the specs. Blue Ridge requires opening each HTML fixture separately.
The cost for all this is that Shenandoah fixtures can't be opened directly in the browser — they can only be opened via the server.
Links
Contributors
- Justin Gehtland
- Geof Dagley
- Larry Karnowski
- Chris Thatcher (for numerous env.js bug fixes!)
- Raimonds Simanovskis
- Jason Rudolph
Copyrights
- Copyright © 2009 Rhett Sutphin, under the MIT license
- Based on blue-ridge, Copyright 2008-2009 Relevance, Inc., under the MIT license
- env.js - Copyright 2007-2009 John Resig, under the MIT License
- Screw.Unit - Copyright 2008 Nick Kallen, license attached
- Rhino - Copyright 2009 Mozilla Foundation, GPL 2.0
- Smoke - Copyright 2008 Andy Kent, license attached