Bundled with infusion is jqUnit, a library modelled on the xUnit API style.
jqUnit wraps base functionality provided by the popular QUnit. All of the QUnit base
functionality remains available in a jqUnit fixture at the QUnit
namespace, and you should consult the QUnit
documentation for details of packaging markup-based fixtures and its own API semantic. However, jqUnit is a complete
wrapper and it is possible and recommended to write complete test suites without reference to QUnit. jqUnit is based on
the 1.x API of QUnit, and not the (currently unreleased) 2.x version of QUnit which is API incompatible with QUnit 1.x.
As well as the use of jqUnit in the browser, there is also a node.js module, node-jqunit which allows the use of the same testing API for writing node.js tests.
A more advanced piece of infrastructure is the IoC Testing Framework which is useful for writing asynchronous test fixtures targeting Infusion's IoC component trees. This is not a replacement for jqUnit but a library layered on top of it which is good for certain specialised purposes. If you are writing plain unit tests, as well as integration tests which don't have a highly asynchronous, conversational style, you should continue to write jqUnit fixtures. If you find you are writing large-scale integration or acceptance tests against significantly-sized parts of an application, that require sequences of asynchronous conversation, for example simulating user GUI interaction or HTTP requests, you should use the IoC Testing Framework.
Organising and controlling fixtures
jqUnit.module(name, hooks)
Starts a group of related tests which will display with the module's name as a prefix. Direct passthrough for QUnit.module
jqUnit.test(name, testFunc)
Registers (queues) a synchronous test fixture by providing a callback which will run it. Direct passthrough for
QUnit.test. This is equivalent to a call to jqUnit.asyncTest
, where the fixture
ends with jqUnit.start
.
jqUnit.asyncTest(name, testFunc)
Registers (queues) an asynchronous test fixture by providing a callback which will run it. Direct passthrough for QUnit.asyncTest.
jqUnit.start()
Restarts QUnit's progress through its fixtures, which were previously suspended by a call to jqUnit.stop()
. The idiom
is that whilst the system is suspended by stop
, the test currently in progress is waiting for I/O and the test run
will not proceed until it resumes. Note that QUnit's suspension has the semantics of a Counting
Semaphore in that repeated
calls to jqUnit.stop
are possible and must be matched by an equal number of calls to jqUnit.start
before the system
will resume. Direct passthrough for QUnit.start.
jqUnit.stop()
Suspends QUnit's progression through its fixtures. QUnit will not continue to the next queued test fixture until it has
been resumed with jqUnit.start
. Direct passthrough for QUnit.start.
jqUnit.expect(count)
Informs QUnit that it must receive a certain number of successful assertions in the current fixture, or else the fixture
will fail. Note that multiple successive calls to jqUnit.expect
within the same fixture will be cumulative - this
is different to QUnit's base behaviour for QUnit.expect
where successive
calls to QUnit.expect
will overwrite the framework's expected count.
count: {Integer}
The number of (additional) successful assertions to be expected
Assertion methods
jqUnit's assertion methods have the xUnit standard signature of [message, expected, actual]
rather than QUnit's
signature of [actual, expected, message]
. In many cases they are direct passthroughs to the QUnit equivalents, but
jqUnit does implement a few useful extra assertion types. In the following documentation we do not describe the standard
message
argument which is accepted as the first argument of every assertion. You may consider that each API section
below contains an entry:
{message: String}
Message describing the assertion
jqUnit.assert(message)
An assertion which unconditionally succeeds, and raises the successful assertion count by one. Equivalent to
QUnit.ok(true, message)
.
jqUnit.fail(message)
An assertion which unconditionally fails, and then aborts the current fixture. Equivalent to QUnit.ok(false, message)
.
jqUnit.assertTrue(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is true
. Equivalent to QUnit.ok(value, message)
.
jqUnit.assertFalse(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is false
. Equivalent to QUnit.ok(!value, message)
.
jqUnit.assertUndefined(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is undefined
.
jqUnit.assertNotUndefined(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is not undefined
.
jqUnit.assertNull(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is null
.
jqUnit.assertNotNull(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is not null
.
jqUnit.assertValue(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is not null
or undefined
.
jqUnit.assertNoValue(message, value)
value {Any}
The value to be tested
Asserts that the supplied value is either null
or undefined
.
jqUnit.assertEquals(message, expected, value)
expected {Any}
The expected value ofvalue
value {Any}
The value to be tested
Asserts that the supplied value is equal to the one supplied as expected
. This will be performed by a strict equality
check (===
) - equivalent to QUnit.strictEqual(actual, expected, message)
jqUnit.assertNotEquals(message, unexpected value)
unexpected {Any}
The value thatvalue
is expected to differ fromvalue {Any}
The value to be tested
Asserts that the supplied value is not equal to the one supplied as unexpected
. This will be performed by a strict
inequality check (!==
) - equivalent to QUnit.notStrictEqual(actual, expected, message)
jqUnit.assertDeepEq(message, expected, value)
expected {Any}
The expected value ofvalue
value {Any}
The value to be tested
Asserts that the supplied value is equal to the one supplied as expected
. This will be performed by a deep equality
check on the basis of properties only (ignoring constructors and prototypes) - equivalent to QUnit.propEqual(actual, expected, message)
jqUnit.assertDeepNeq(message, unexpected, value)
unexpected {Any}
The value thatvalue
is expected to differ fromvalue {Any}
The value to be tested
Asserts that the supplied value is not equal to the one supplied as unexpected
. This will be performed by a deep
equality check on the basis of properties only (ignoring constructors and prototypes) -
equivalent to QUnit.notPropEqual(actual, expected, message)
jqUnit.assertCanoniseEqual(message, expected, value, canonFunc)
expected {Any}
The expected value ofvalue
value {Any}
The value to be testedcanonFunc {Function: (value {Any}) → Any}
A canonicalisation function which will be applied to bothexpected
andvalue
to reduce them to a common form in which they can then be compared by standard deep equality.
Asserts that the supplied value is equal to the one supplied as expected
, by deep equality and after applying a
"canonicalisation function" to remove irrelevant differences between the two values. Useful canonicalisation functions
could act i) to allow all Functions to compare equal, ii) to remove irrelevant differences in array order by sorting, or
iii) other means. Functions supplied by jqUnit include jqUnit.canonicaliseFunctions
and jqUnit.sortTree
.
jqUnit.assertLeftHand(message, expected, value)
expected {Any}
An expected subset ofvalue
value {Any}
The value to be tested
Assert that the actual value object is a superset (considered in terms of shallow key coincidence) of the expected value
object. The coincidence between value
and expected
is only in terms of top-level keys, but the comparison will use
deep equality. That is, if value
has any top-level keys in common with expected
, they must compare equal by deep
equality - but it may have extra top-level keys whose contents will be ignored. "Left hand" (expected) is a subset of
actual.
jqUnit.assertRightHand(message, expected, value)
expected {Any}
An expected superset ofvalue
value {Any}
The value to be tested
Assert that the actual value object is a subset (considered in terms of shallow key coincidence) of the expected value
object. This is the natural converse of jqUnit.assertLeftHand
but this assertion is rarely used - it is less useful to
assert that a payload is as expected but may be missing arbitrarily many top-level keys.
jqUnit.expectFrameworkDiagnostic(message, toInvoke, errorTexts)
Assert that the supplied callback will produce a framework diagnostic (that is, an exception descended from
fluid.FluidError
), containing the supplied text(s) somewhere in its error message - that is, the callback has invoked
fluid.fail
with a message containing the entries in errorTexts
.
message {String}
The message prefix to be supplied for all the assertions this function issuestoInvoke {Function}
A no-arg function holding the code to be tested for emission of the diagnosticerrorTexts {String|Array of String}
Either a single string or array of strings which themessage
> field of the thrown exception will be tested against - each string must appear as a substring in the text
Utilities for testing
jqUnit.canonicaliseFunctions(value)
A canonicalisation function, helpful for use with
jqUnit.assertCanoniseEqual - this will take any
Functions within the supplied tree and replace them with the same Function reference (fluid.identity
)
value {Object}
Value to be canonicalised- Returns:
{Object}
Deep clone ofvalue
with all functions replaced by the same reference
jqUnit.sortTree(value)
A canonicalisation function, helpful if supplying a renderer component tree to
jqUnit.assertCanoniseEqual - this will sort each
set of children
in the tree recursively into a canonical order, where this order would not disturb the rendered
result.
Testing in the browser
The QUnit browser UI requires a standard set of includes and markup to render properly. In the header (adjust include paths as appropriate):
<link rel="stylesheet" media="screen" href="../../../lib/qunit/css/qunit.css" />
<script type="text/javascript" src="../../../lib/qunit/js/qunit.js"></script>
<script type="text/javascript" src="../../../test-core/jqUnit/js/jqUnit.js"></script>
and in the body of the document:
<h1 id="qunit-header">Your Test Name Here</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<!-- Test HTML -->
<div id="qunit-fixture">
</div>
Mysteriously, these UI element ids and their functions are not documented on the QUnit site itself - although you can
find explanations of them in some 3rd party tutorials. Any markup used within test fixtures must be placed within the
div with id qunit-fixture
- QUnit will take care of tearing this down and restoring it in its original condition
before the start of every test.
Your test fixtures should be scheduled to start only once this markup has loaded - you can achieve this by either
starting them within a $(document).ready callback, or by writing a <script>
block at
the base of your HTML file which starts them.
jqUnit
provides a special set of assertions and utilities for testing within the browser environment:
jqUnit.assertNode (message, expected, node)
Checks a subtree of DOM nodes descended for a particular node against a condensed JSON representation, allowing multiple
aspects of a rendered UI to be checked in a single operation. Each attribute of each DOM node is attached as a direct
property, the node's tag name is attached as nodeName
, the node's element test is attached as nodeText
, and then all
children are recursively attached as the property children
. In addition, at any stage, the entire nested markup may be
attached as nodeHTML
.
As an example, the following markup:
<a href="a-link"><img src="a-source"/></a>
will compare equal to
{
nodeName: "a",
href: "a-link",
children: [
{
nodeName: "img",
src: "a-source"
}
]
}
expected {Object|Array}
A condensed JSON representation of a set of assertions to make about a subtree of DOM nodes, or an array of thesenode {DOM|Array of DOM|jQuery}
The DOM node to be checked againstexpected
- this may also be an array of DOM nodes or a jQuery object.
jqUnit.canonicaliseDom(list)
Canonicalise a list of DOM elements (or a jQuery) by converting elements to their ids (allocated if necessary).
list {Array of DOM|jQuery}
An array of DOM nodes or a jQuery- Returns: {Array of String} An array of ids for the supplied nodes, allocated via fluid.allocateSimpleId.
jqUnit.assertDomEquals(message, expected, actual)
Compare two lists of DOM elements (or jQueries) for being equal by virtue of containing the same DOM elements in the
same order. This will be achieved by canonicalising the DOM elements onto their ids by means of the canonicalisation
function jqUnit.canonicaliseDom
expected {Array of DOM|jQuery}
The expected list of DOM nodesactual {Array of DOM|jQuery}
The actual list of DOM nodes to be compared withexpected
.
jqUnit.isVisible(msg, selector)
Asserts that the DOM nodes identified by selector
are visible, in terms of not matching the jQuery
:hidden
pseudoselector.
selector {jQueryable}
The selector or other jQueryable identifying the DOM nodes to be tested for visibility
jqUnit.notVisible(msg, selector)
Asserts that the DOM nodes identified by selector
are not visible, in terms of matching the jQuery
:hidden
pseudoselector.
selector {jQueryable}
The selector or other jQueryable identifying the DOM nodes to be tested for invisibility
jqUnit.assertNodeExists (msg, selector)
Asserts that there is at least one node matching the provided selector (or other jQueryable)
selector {jQueryable}
The selector or other jQueryable identifying the DOM nodes to be checked for existence
jqUnit.assertNodeNotExists (msg, selector)
Asserts that there are no nodes matching the provided selector (or other jQueryable)
selector {jQueryable}
The selector or other jQueryable identifying the DOM nodes to be checked for nonexistence
jqUnit.subvertAnimations()
Overrides jQuery's animation routines to be synchronous. This can simplify tests which would otherwise have to wait for
an unknown timeout for the DOM to come into an expected state after, say, a jQuery.hide
or jQuery.show
.