TestMore - A framework for writing test scripts
plan('tests', numTests);
# or
plan('no_plan');
# or
plan('skip_all', reason);
# Various ways to say "ok"
ok(got == expected, testDescription);
is(got, expected, testDescription);
isnt(got, expected, testDescription);
# Rather than document.write("# here's what went wrong\n")
diag("here's what went wrong");
like(got, /expected/, testDescription);
unlike(got, 'expected', testDescription);
cmpOk(got, '==', expected, testDescription);
isDeeply(complexStructure1, complexStructure2, testDescription);
canOk(module, @methods);
isaOk(object, class);
pass(testDescription);
fail(testDescription);
# Utility comparison functions.
eqArray(\@got, \@expected);
eqAssoc(\%got, \%expected);
eqSet(\@got, \@expected);
STOP! If you're just getting started writing tests, have a look at TestSimple first. This is a drop in replacement for TestSimple 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 ok()
function, it doesn't provide good diagnostic output.
Before anything else, you need a testing plan. 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 plan()
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('no_plan');
You can also calculate the number of tests:
plan('tests' someArray.length);
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.
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.
ok(got eq expected, testDescription);
got eq expected
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. ok( exp{9} == 81, 'simple exponential' );
ok( p.tests == 4, 'saw tests' );
ok( items.length, 'items populated');
testDescription
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. testDescription
is optional, but we very strongly encourage its use. not ok 18 - sufficient mucus
# Failed test 18 (foo.t at line 42)
is ( got, expected, testDescription );
isnt( got, expected, testDescription );
// Is the ultimate answer 42?
is( ultimateAnswer(), 42, "Meaning of Life" );
// foo isn't empty
isnt( foo, '', "Got some foo" );
ok( ultimateAnswer() eq 42, "Meaning of Life" );
ok( foo != '', "Got some foo" );
var foo = 'waffle', bar = 'yarblokos';
is( foo, bar, 'Is foo the same as bar?' );
not ok 17 - Is foo the same as bar?
# Failed test (foo.t at line 139)
# got: 'waffle'
# expected: 'yarblokos'
true
or false
! // XXX BAD!
is( isFinite(brooklyn['trees']), true, 'Brooklyn has finite trees');
isFinite(brooklyn['trees'])
is true, it checks if it returns true
. Very different. Similar caveats exist for false
and 0. In these cases, use ok(). is( isFinite(brooklyn['trees']), 'Brooklyn has finite trees');
like( got, /expected/, testDescription );
/expected/
. like(got, /expected/, 'got is expected');
ok( /expected/.test(got), 'got is expected');
//
or new RegExp()
) or as a string that looks like a regex: like(got, 'expected', 'got is expected');
unlike( this, /that/, testDescription );
cmpOk( got, op, expected, testDescription );
// 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...
not ok 1
# Failed test (foo.html at line 12)
# '23'
# &&
# undef
cmpOk( bigHairyNumber, '==', anotherBigHairyNumber );
canOk(class, method, method, method...);
canOk(object, method, method, method...);
canOk('Foo', 'foo', 'bar', 'whatever');
for (var meth in ['foo', 'bar', 'whatever']) {
canOk('Foo', meth);
}
isaOk(object, class, objectName);
object
is a class
. Also checks to make sure the object was defined in the first place. Handy for this sort of thing: var obj = new SomeClass();
isaOk( obj, 'SomeClass' );
var obj = new SomeClass();
ok( defined obj && SomeClass.constructor.prototype.isPrototypeOf(obj));
isaOk( [], 'Array' );
pass(testDescription);
fail(testDescription);
If you pick the right test function, you'll usually get a good idea of what went wrong when it fails. But sometimes it doesn't work out that way. So here we have ways for you to write your own diagnostic messages.
diag(message);
diag(message, message2, message3, ...);
ok( users['foo']), "There's a foo user" )
|| diag("Since there's no foo, check that /etc/bar is set up right");
not ok 42 - There's a foo user
# Failed test (foo.html at line 52)
# Since there's no foo, check that /etc/bar is set up right.
plan('tests', 1, 'no_diag');
. This is useful if you have diagnostics for personal testing but then wish to make them silent for release without commenting out each individual statement.Not everything is a simple equality check or regular expression comparison. There are times you need to see if two arrays are equivalent, for instance. For these instances, TestMore provides a handful of useful functions.
isDeeply( got, expected, testDescription );
got
and expected
are arrays or objects, it does a deep comparison, walking each data structure to see if they are equivalent. If the two structures are different, isDeeply() will output diagnostics identifying where they start differing. eqArray(got, expected);
eqAssoc(got, expected);
eqHash(got, expected);
eqSet(this, that);
Sometimes the TestMore interface isn't quite enough. Fortunately, TestMore is built on top of TestBuilder, which provides a single, unified back end for any test library to use. This means two test libraries that both use TestBuilder can be used together in the same program.
If you simply want to do a little tweaking of how the tests behave, you can access the underlying TestBuilder object like so:
var testBuilder = TestMore.builder();
Michael G Schwern <schwern@pobox.com> with much inspiration from Joshua Pritikin's Test module and lots of help from Barrie Slaymaker, Tony Bowden, blackstar.co.uk, chromatic, Fergal Daly and the perl-qa gang. JavaScript implementation by David Wheeler <david@kineticode.com>.
Copyright 2001, 2002, 2004 by Michael G Schwern <schwern@pobox.com>, 2005 by David Wheeler.
This program is free software; you can redistribute it and/or modify it under the terms of the Perl Artistic License or the GNU GPL.
See http://www.perl.com/perl/misc/Artistic.html and http://www.gnu.org/copyleft/gpl.html.