Jarvis is a unit testing framework written in JavaScript. It’s based heavily off of the API of NUnit, which is a rewrite of a port of JUnit which is a port of SUnit. Eventually, there will be no new code: just rewrites of old code.
npm install jarvis-test -g
Here is a quick taste of what writing a test using Jarvis looks like:
// mytest.js module.exports = (function(){ var obj; return { setup: function() { obj = new MyClass(); }, tearDown: function() { obj.destruct(); obj = null; }, test: function My_test() { return [ function Should_have_foo_property() { Assert.that(obj, Has.property('foo').equalTo('bar')); }, function Should_be_valid() { Assert.that(obj.isValid(), Is.True()); }, function Should_be_ignored() { Assert.ignore('I am ignored'); }, function Should_catch_error() { Assert.willThrow(); throw new Error(); } ]; } }; }());
# command line jarvis /path/to/mytest.js
// mytest.js module.exports = (function(){ var obj; return { setup: function(setupComplete) { obj = new MyClass(); setupComplete(); }, tearDown: function(tearDownComplete) { obj.destruct(function() { obj = null; tearDownComplete(); }); }, test: function My_test() { return [ function Should_have_foo_property(testComplete) { Assert.begin() .that(obj, Has.property('foo').equalTo('bar')) .run(testComplete); }, function Should_be_valid(testComplete) { Assert.begin() .that(obj.isValid(), Is.True()) .run(testComplete); }, function Should_be_ignored(testComplete) { testComplete({ ignore: 'I am ignored' }); }, function Should_catch_error(testComplete) { Assert.willThrow(); testComplete(new Error()); } ]; } }; }());
# command line jarvis --async /path/to/mytest.js
function MyClass() { this.defaultName = "world"; this.sayHello = function(name) { if (name === undefined) { name = this.defaultName; } if (typeof(name) !== "string") { throw "MyClass.bar() expects a string for the name"; } return "Hello, " + name + "!"; }; this.appendToFoo = function(text) { document.getElementsByTagName("foo")[0].appendChild(document.createTextNode(text || "")); }; } function getTests() { var myClass; return { setup: function() { myClass = new MyClass(); }, tearDown: function() { myClass = null; }, test: function MyClass_tests() { return [ function Default_name_should_be_world() { Assert.that(myClass.defaultName, Is.equalTo("world")); }, function SayHello_should_emit_proper_punctuation() { Assert.that(myClass.sayHello(), Is.regexMatch(/^.+?,\s+.+!$/)); }, function How_about_a_sweet_looking_diff() { Assert.that(myClass.sayHello("good looking"), Is.equalTo("He'll do good on lookout")); }, function SayHello_with_no_parameters_should_use_default_name() { Assert.that(myClass.sayHello(), Is.equalTo("Hello, world!")); }, function Do_something_awesome() { Assert.ignore("This class doesn't do anything awesome, yet"); }, function Oops_an_error() { myClass.sayHello(20); }, function SayHello_should_not_allow_non_strings() { Assert.willThrow("MyClass.bar() expects a string for the name"); myClass.sayHello(20); }, { setup: function() { document.body.appendChild(document.createElement("foo")); }, tearDown: function() { document.body.removeChild(document.body.getElementsByTagName("foo")[0]); }, test: function Tests_on_foo() { return [ function Should_set_the_text_of_foo() { myClass.appendToFoo("this is the text"); Assert.that("body > foo", Is.inDom()); Assert.that("body > foo", Has.text.equalTo("this is the text")); }, function Should_set_the_text_of_foo_to_empty_string() { myClass.appendToFoo(); Assert.that("body > foo", Is.inDom()); Assert.that("body > foo", Has.text.empty); } ]; } } ]; } }; } //and run the tests Jarvis.run(getTests());
Jarvis tests are just simple functions that run whatever code you
want. To run the test, pass the function to Jarvis.run()
. That’s it.
If running tests using Node, there are several more options. You can still manually run tests using
Jarvis.run()
, but you can also run tests from the command line,
which is probably preferred. After installing the npm module globally, run jarvis --help
to view detailed options on how to use Jarvis from the command line.
Assertions are made in a slightly more verbose but more readable format. To assert
something, make a call to Assert.that(actual, constraint)
.
actual
is the actual value for the comparison, and constraint is the return
value of a call to one of the Is
or
Has
functions. What you end up with is something that looks like this:
Assert.that(foo, Is.equalTo(bar)); Assert.that(foo, Has.property("length").greaterThan(1));
Jarvis will automatically use the name of the function as the name of the test, so it would behoove you to name your functions (as opposed to passing an anonymous function).
To create a test suite, have your test function return an array of test functions. See the example above for a concrete example. Jarvis will automatically handle child tests to an arbitrary depth.
To invoke a setup and/or tear down function before each test, pass an object to
Jarvis.run()
. If the object has a setup
property, that
will be the function that is run before each test. If the object has a tearDown
(notice the capital D) property, that function will be run after
each test. The actual test should reside in the test
property.
Jarvis
objectJarvis.run(function [, reporter])
Jarvis.run()
is the function that does all the work of running the
test. The first argument is a function reference. It also accepts an optional
second argument to specify a reporter. If omitted, it
defaults to Jarvis.defaultReporter
.
Jarvis.summary([reporter])
Jarvis.summary()
prints a summary of all tests that have been run
so far using the given reporter
.
Jarvis.defaultReporter
The defaultReporter
property dictates how the results of the test are reported
back to you, if not specified in the second argument of Jarvis.run()
.
Jarvis comes bundled with two reporters:
HtmlReporter
(default):
reports test results as HTML. Example output can be seen in the example
above.
ConsoleReporter
: reports test
results to the debug console on Firefox (with Firebug), Chrome, Safari and Opera (with Dragonfly).
CliReporter
: reports test
results to command line. This reporter is only usable on the server-side (e.g. Node).
Jarvis.htmlDiffs
A boolean flag indicating whether to generate HTML diffs or not. Default is
true
.
Note: HTML diffs will only be generated when comparing two
strings using Is.equalTo()
or
Is.identicalTo()
.
Jarvis.showStackTraces
A boolean flag indicating whether to show stack traces for errors and failures. Default is
true
.
This option is only relevant for the HtmlReporter
on
browsers other than Opera (Opera takes a long time to compute the stack trace so it’s
always disabled).
Jarvis uses the stack trace library detailed here.
Assert
objectAssert.that(actual, constraint)
Assert.that()
is the main entry point for making assertions.
actual
is the value to compare against. constraint
is
the return value of a call to one of the Is
or
Has
functions.
Assert.that("foo", Is.equalTo("foo")); Assert.that("foobar", Is.not.regexMatch(/Foo/)); Assert.that([1, 2, 3], Has.value(2)); Assert.that({ foo: "bar" }, Has.key("foo")); Assert.that({ foo: 7 }, Has.property("foo").greaterThan(5));
Assert.willThrow([errorObject])
Assert.willThrow()
is equivalent to declaring an expected
exception in other unit testing frameworks. If an error is raised after
calling Assert.willThrow()
, Jarvis will
not report an error back. If an error is not raised, the test will fail.
If errorObject
is given, Jarvis will verify
that the thrown object is equal to errorObject
.
Assert.ignore([message])
A call to Assert.ignore()
ceases execution of the test. This will
not be reported back as an error.
Assert.fail([message])
A call to Assert.fail()
ceases execution of the test and is reported
back as a failed test.
Is
objectIs.equalTo(expected)
This constraint evaluates for equality. What does equality mean? Well, it’s a little bit more relaxed to allow for more intuitive usage. The easiest way to figure out everything is to look at the tests of Jarvis itself. Yes, Jarvis uses Jarvis to test itself. There’s a yo dawg joke in here somewhere…
Here’s a summary of the non-obvious caveats:
RegExp
objects are equal if they wrap the same regular expression (including modifiers)null
is equal to undefined
true
is equal to 1
false
is equal to 0
false
is equal to an empty stringIs.identicalTo(expected)
This constraint simply performs actual === expected
.
Is.lessThan(expected)
This constraint simply performs actual < expected
.
Is.lessThanOrEqualTo(expected)
This constraint simply performs actual <= expected
.
Is.greaterThan(expected)
This constraint simply performs actual > expected
.
Is.greaterThanOrEqualTo(expected)
This constraint simply performs actual >= expected
.
Is.regexMatch(regex)
This constraint verifies that actual
matches the given regex
.
Assert.that("foo", Is.regexMatch(/foo bar/)); Assert.that("foo", Is.regexMatch(new RegExp("foo bar")));
Is.NULL()
This constraint simply performs actual === null
.
Is.TRUE()
This constraint simply performs actual === true
.
Is.FALSE()
This constraint simply performs actual === false
.
Is.undefined()
This constraint simply performs actual === undefined
.
Is.empty()
This constraint verifies that actual
is empty. In Jarvis,
an object is considered empty if it is:
null
undefined
Is.inDom()
This constraint verifies that the selector given by actual
is present
in the DOM.
Jarvis uses the Sizzle CSS selector
engine, which is also the selector engine used by jQuery.
That means any selector that you would pass to the jQuery
object is valid.
Assert.that("#foo > input[type='text']", Is.inDom());
Is.not
This negates any constraint defined above by chaining them together. For example,
Is.not.empty()
will negate the empty constraint.
Has
object
Has
is used for performing assertions on objects and arrays.
It is a substitute for Is
and is used in the same way.
Has.value(value)
This constraint verifies that the collection contains a value
equal to the given value
.
Property names are ignored.
This constraint can be negated.
Assert.that([2, 4, 6], Has.value(4)); Assert.that([2, 4, 6], Has.no.value(3));
Has.key(key)
This constraint verifies that the collection contains a property with a name
equal to the given key
.
Property values are ignored.
This constraint can be negated.
Assert.that([2, 4, 6], Has.key(0)); Assert.that({ foo: 1, bar: 2 }, Has.key("foo")); Assert.that({ foo: 1, bar: 2 }, Has.no.key("qux"));
Has.property(name)
This function returns an interface identical to Is
.
It evaluates a constraint on the value of the property given by name
.
This constraint cannot be negated.
Assert.that({ foo: 1, bar: 2 }, Has.property("foo").equalTo(1));
Has.text()
This property returns an interface identical to Is
.
It evaluates a constraint on the value of the text inside the DOM node identified by
a selector.
This operates on the notion that the given selector matches an element whose first child is a DOM text node. Otherwise it will not match.
This constraint can be negated.
Assert.that("div > span.error", Has.text().equalTo("An error occurred"));
Has.flattenedText()
This property is identical to Has.text
except that it will
recursively go through each child node and append all text nodes.
For example, this assertion:
Assert.that("div > span.error", Has.flattenedText().equalTo("An error occurred"));
will pass for this HTML
<html> <body> <div> <span class="error">An <span>error <span>occurred</span></span></span> </div> </body> </html>
This constraint can be negated.
The ConsoleReporter
reports test results to the console. This works on Firefox, Opera and Chrome.
IE9’s console doesn’t support group
so it just throws errors.
ConsoleReporter
on Firefox (with Firebug):
ConsoleReporter
on Chrome:
ConsoleReporter
on Opera (with Dragonfly):
HtmlReporter
The HtmlReporter
renders a color-coded interface. If a test has child tests,
it will collapse them to save space. You can drill down into each child test by clicking
on the test name.
The HtmlReporter
constructor takes two optional arguments. The first argument
is a DOM node to append the generated HTML to. If not given, it defaults to document.body
.
The second argument is an options object. The only option available currently is collapsedByDefault
which defaults to true
and will collapse all
child tests by default.
An example of the HtmlReporter
can be seen above or on
Jarvis’ own test report.
CliReporter
The CliReporter
renders reports similar to other xUnit CLI interfaces:
a dot is a passed test, an "E" is an error, an "I" is an ignored test and an "F" is
a failed test. An optional --verbose
flag can be passed to the jarvis
executable to display more information about each test.
Jarvis was written by Tommy Montgomery. You can reach him at tmont@tmont.com.
Jarvis makes use of the following open source libraries:
Report bugs or suggest enhancements either via email or on Github.