=head1 Name
Test.More - A framework for writing test scripts
=head1 Synopsis
=head1 Description
B If you're just getting started writing tests, have a look at
L first. This is a drop in replacement for Test.Simple
that you can switch to once you get the hang of basic testing.
The purpose of this module is to provide a wide range of testing utilities.
Various ways to say "ok" with better diagnostics, facilities to skip tests,
test future features and compare complicated data structures. While you can do
almost anything with a simple C function, it doesn't provide good
diagnostic output.
=head2 I love it when a plan comes together
Before anything else, you need a an HTML element in which to run the tests,
and you need a testing plan. The HTML element must have the ID "test" so that,
when all test finish running, Test.More can check everything over and possibly
output a test status message in the event that something has gone wrong.
A testing plan declares how many tests your script is going to run to protect
against premature failure. The preferred way to create a testing plan is to
use the C function:
plan({ tests: numTests });
There are rare cases when you will not know beforehand how many tests your
script is going to run. In this case, you can declare that you have no plan.
(Try to avoid using this as it weakens your test.)
plan({ noPlan: true });
In some cases, you'll want to completely skip an entire testing script.
plan({ skipAll: reason });
Your script will declare a skip with the reason why you skipped and exit
immediately with a zero (success).
You can also calculate the number of tests:
plan({ tests: someArray.length });
or to decide between running the tests at all:
if (!window.XMLHttpRequest) {
plan({ skipAall: 'Test irrelevant in IE' });
} else {
plan({ tests: 42 });
}
=head2 Test Descriptions
By convention, each test is assigned a number in order. This is largely done
automatically. However, it's often very useful to assign a description to each
test. Which would you rather see:
ok 4
not ok 5
ok 6
or
ok 4 - basic multi-variable
not ok 5 - simple exponential
ok 6 - force == mass * acceleration
The latter gives you some idea of what failed. It also makes it easier to find
the test in your script: simply search for "simple exponential".
All test functions take a description argument. It's optional, but highly
suggested that you use it.
=head2 I'm ok, you're not ok.
The basic purpose of this library is to print out either "ok #" or "not
ok #" depending on whether a given test succeeded or failed. Everything
else is just gravy.
All of the following functions print "ok" or "not ok" depending on if the test
succeeded or failed. They all also return true or false, respectively.
=over
=item B
ok(got == expected, testDescription);
This function simply evaluates any expression (C is just a
simple example) and uses its value to determine whether the test succeeded or
failed. A true expression passes, a false one fails. Very simple.
For example:
ok( exp{9} == 81, 'simple exponential' );
ok( p.tests == 4, 'saw tests' );
ok( items.length, 'items populated');
(Mnemonic: "This is ok.")
C is a very short description of the test to be printed out.
It makes it very easy to find a test in your script when it fails and gives
others an idea of your intentions. C is optional, but we
B strongly encourage its use.
Should an ok() fail, it will produce some diagnostics:
not ok 18 - sufficient mucus
# Failed test 18 (foo.t at line 42)
This is actually the same as Test.Simple's ok() function.
=item B
=item B
is ( got, expected, testDescription );
isnt( got, expected, testDescription );
Similar to ok(), is() and isnt() compare the stringified value of their two
arguments and use the result of that to determine if the test succeeded or
failed. So these:
// Is the ultimate answer 42?
is( ultimateAnswer(), 42, "Meaning of Life" );
// foo isn't empty
isnt( foo, '', "Got some foo" );
are similar to these:
ok( ultimateAnswer() == 42, "Meaning of Life" );
ok( foo != '', "Got some foo" );
(Mnemonic: "This is that." "This isn't that.")
So why use these functions? They produce better diagnostics on failure than
does ok(). ok() cannot know what you are testing for (beyond the name), but
is() and isnt() know what the test was and why it failed. For example this
test:
var foo = 'waffle', bar = 'yarblokos';
is( foo, bar, 'Is foo the same as bar?' );
Will produce something like this:
not ok 17 - Is foo the same as bar?
# Failed test (foo.t at line 139)
# got: 'waffle'
# expected: 'yarblokos'
So you can figure out what went wrong without rerunning the test.
You are encouraged to use is() and isnt() over ok() where possible, however do
not be tempted to use them to find out if something is C or C!
// XXX BAD!
is( isFinite(brooklyn['trees']), true, 'Brooklyn has finite trees');
This example fails to check for whether C is
true, it checks if it returns C. Very different. Similar caveats exist
for C and 0. In these cases, use ok().
is( isFinite(brooklyn['trees']), 'Brooklyn has finite trees');
=item B
like( got, /expected/, testDescription );
Similar to ok(), like() matches this against the regex C.
So this:
like(got, /expected/, 'got is expected');
is similar to:
ok( /expected/.test(got), 'got is expected');
(Mnemonic "This is like that".)
The second argument is a regular expression. It may be given as a literal
regex object (i.e. C/> or C) or as a string that looks like a
regex:
like(got, 'expected', 'got is expected');
If is not a string, the test will fail.
like([], /expected/, 'this will fail');
The advantages of like() over ok() are similar to that of is() and isnt().
Better diagnostics on failure.
=item B
unlike( this, /that/, testDescription );
Works exactly as like(), only it checks if this B match the
given pattern.
=item B
cmpOK( got, op, expected, testDescription );
Halfway between ok() and is() lies cmpOK(). This function allows you to
compare two arguments using any binary JavaScript operator, plus a few
Perl-style binary operators for string comparisons:
// ok( got.toString() == expected.toString() );
cmpOK( got, 'eq', expected, 'got eq expected' );
// ok( got == expected );
cmpOK( got, '==', expected, 'got == expected' );
// ok( got && expected );
cmpOK( got, '&&', expected, 'got && expected' );
// ...etc...
The advantage of cmpOK() over ok() is when the test fails you'll know what
this and that were:
not ok 1
# Failed test (foo.html at line 12)
# '23'
# &&
# undef
It's also useful in those cases where you are comparing numbers and is()'s use
of string comparison will interfere:
cmpOK( bigHairyNumber, '==', anotherBigHairyNumber );
The added perl operators are:
=over
=item eq
String equivalence.
=item ne
String non-equivalence.
=item lt
String less than.
=item gt
String greater than.
=item le
String less than or equal to.
=item ge
String greater than or equal to.
=back
=item B
canOK(class, method, method, method...);
canOK(object, method, method, method...);
Checks to make sure the class or object can do the defined methods.
canOK('Foo', 'foo', 'bar', 'whatever');
Handy for quickly testing an interface.
No matter how many methods you check, a single canOK() call counts as one
test. If you desire otherwise, use:
for (var meth in ['foo', 'bar', 'whatever']) {
canOK('Foo', meth);
}
B Instances of classes defined by assigning anonymous objects to the
C attribute of the constructor will not work properly in the
C function.
My.Class = function () {};
My.Class.prototype = {
foo: function () { return 'foo' },
bar: function () { return 'bar' }
};
var my = new My.Class();
canOK(my, 'foo'); // Fail
This is because the anonymous object assigned to the prototype is constructed
by the base C