Drive.js
Cross-browser unit testing for humans.
Installation
- Download node.js
- Open up a terminal.
- Run the following command:
$ npm install -g drive.js
You should be good to go!
What is it?
Drive is a unit testing framework and test driver. It can be used locally -- without any browsers involved --
or set up as a test driver server for other clients to connect to.
The goal of Drive is to reduce context-switching from your terminal -- ideally, you should be able to test all
of your target environments without ever leaving your terminal. This means that it attempts to provide (at minimum)
a decent set of debugging tools across all environments:
-
Humane stack traces: reformats stack traces (as much as possible) to give context around the exceptions, using
tracejs. Some environments support a bit less debugging info than V8, and in those cases Drive will do its level
best to at least show you where the error originated from.
-
console.log piping: Drive will pipe the output from console.error
and console.log
directly to your terminal,
annotated with the environment from which they originated. This means that if you need (for whatever reason) to inspect
variables in a given environment, it's totally possible without too much hand-wringing.
-
Experimental repl
support: You can set a breakpoint using __repl.set_trace()
in your code, and drive will enable
a gdb
-style REPL in that environment from that point forward.
The secondary goal of drive is that it should make testing DOM-heavy applications easy.
This means that you may specify your own HTML to be served with a given test suite. The drive server will take care of serving it
for you. If you have an AJAX-heavy application, you can additionally supply stubbed endpoints that the Drive server will serve.
What does a test suite look like?
Drive is modeled after visionmedia's mocha.
It provides the assert module automatically.
There should be only one suite
per file.
suite("Test suite name", function(require) {
var MyApp = require('../myapp')
test("A synchronous test", function() {
assert.equal(0, 1)
})
test("An async test", function(done) {
var xhr = new XMLHttpRequest
xhr.open('GET', 'myendpoint')
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
assert.equal(xhr.responseText, 'hello world')
done()
}
}
xhr.send(null)
})
})
html('../path/to/my/document.html')
endpoints({
'myendpoint':function(req, resp) {
resp.writeHead(200, {'Content-Type':'text/plain'})
resp.end('hello world')
}
})
CLI
drive file_or_directory [file_or_directory, ...]
file_or_directory
is the relative path to the file you want to test.
Run the tests located at file_or_directory
locally, in a JSDOM environment.
If a directory is given, drive
will search for any files matching *.test.js
under
that directory and its sub-directories.
No drive server is required in this mode.
drive file_or_directory [...] --driver=(host | port)
Runs the tests listed in all environments available to the Drive server located at host
or port
.
drive file_or_directory [...] --driver 8124 --want env [--want env, ...]
Runs the tests listed in all environments matching env
, where env is transformed into a
regex that matches against browser / version / os
Examples:
drive . --driver 8124 --want ie --want firefox
drive . --driver 8124 --want ie/8
drive . --driver 8124 --want osx
drive file_or_directory [...] --driver 8124 --repl
Will run the tests at file_or_directory
in a single environment (whichever matches first, so it's
advisable to use --want
to filter down environments) with repl
mode enabled.
This passes all test files (and all require
'd media) through node-burrito
, transforming it to
provide breakpoints to the repl (for step-debugging).
Commands in the repl:
n
: step one statement forward. do not enter function calls.s
: step one statement forward, enter function calls if any.c
: continue execution.
Any statements not matched by the above commands will be evaluated in the same context.
The last item of arguments
will be the REPL
function -- don't be alarmed!
drive file_or_directory [...] --browserify
Will build a browserify bundle
of test dependencies and use it to provide requirements instead of the default
require.js provider. Because this method does not use require.js, it is not
necessary to accept require
as an argument in your suite
function. If your
tests do have this, though, do not fret as it will be rewritten to remove it.
Drive also supports passing options to browserify for customizing your test
bundle output.
-
drive [...] --browserify -- --transform <file>
: Apply <file>
as a
browserify transform to all dependencies. Note that all transforms are applied
to test files as well. Alias -t
.
-
drive [...] --browserify -- --noparse <file>
: Skip browserify's parsing of
<file>
, useful for large libraries or other files that do not export or
require anything.
-
drive [...] --browserify -- --entry <file>
: Manually add <file>
as an
entry point in your browserify bundle. Alias -e
.
-
drive [...] --browserify -- --external <file>
: Consider <file>
an
external resource to require
from. Alias -x
.
drive file_or_directory [...] --tap
Test runs will output machine-friendly
TAP instead of its default,
more human-friendly output. This is useful for integrating Drive into
automated testing/building systems.
drive file_or_directory [...] --failfast
Test runs will be immediately ended upon any single failure. Alias -x
drive --spawn (host | port)
Spawns a drive test server.
Clients may be registered by navigating to http://host:port/
in your desired environments.
A DNode server will attempt to listen one port above the given port for incoming test requests.
drive --spawn 8124 [--bundle <file or dir>, ...]
: Automatically bundle these files in every
test, regardless of default or custom HTML for that test. Useful for pinning to a version of jQuery,
or ensuring that all tests run with es5-shim
. These files may be cached by the browser.
If a directory is provided, all files ending in .js
in that directory and its subdirectories will
be included in the bundle.
NB: If there is a .drive_bundle
file in the current directory or any parent directory,
those files will be automatically bundled, relative to the location of the .drive_bundle
:
Example .drive_bundle
, which will include all .js
files in js/thirdparty
, as well as jquery.js
:
./js/thirdparty/
./js/jquery.js
NB: Bundled files will be excluded from any processing done by the drive server -- that is to
say, you cannot use the repl
inside these files (and in the future, no coverage or perf data
will be available within them).
drive --spawn 8124 [--adaptor file.js, ...]
: If given, after the Driver server is spawned, passes
the server to the function exported from file.js
. Listeners may be attached to the driver server
to extend functionality. Multiple adaptors may attach to the same server.
module.exports = function(driver) {
driver.on('join', function(env) {
})
driver.on('drop', function(env) {
})
driver.on('request', function(request) {
})
driver.on('run', function(run, env) {
})
driver.on('suite', function(run, env, suite) {
})
driver.on('result', function(run, env, suite, result) {
})
driver.on('finish', function(run, env) {
})
}
Some specifics
-
Driver will attempt to divide-and-conquer your test files in up to four environments at once --
that is to say, if you have four instances of a single browser connected to a Drive server, and you
send them 32 files to test, each browser will test 8 different files, in an effort to keep things speedy.
-
Tests (and require
'd media) are hosted under /<env_uuid>/<run_uuid>/_media/
, so they should never
be cached.
-
User endpoints are similarly hosted under /<env_uuid>/<run_uuid>/
.
-
Registered environments will attempt to reconnect to the server if they detect that it is down. That
means, in practice, that you can restart the driver server itself without having to refresh 4-5 different
browsers.
-
Works on IOS.