Name

TestMore - A framework for writing test scripts

Synopsis

  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);

Description

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.

I love it when a plan comes together

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);

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.

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.

ok

  ok(got eq expected, testDescription);
This function simply evaluates any expression (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.

For example:

    ok( exp{9} == 81,   'simple exponential' );
    ok( p.tests == 4,   'saw tests' );
    ok( items.length,  'items populated');
(Mnemonic: "This is ok.")

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.

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 TestSimple's ok() function.

is

isnt

  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() eq 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 true or false!

  // XXX BAD!
  is( isFinite(brooklyn['trees']), true, 'Brooklyn has finite trees');
This example fails to check for whether 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

  like( got, /expected/, testDescription );
Similar to ok(), like() matches this against the regex /expected/.

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. // or new RegExp()) or as a string that looks like a regex:

    like(got, 'expected', 'got is expected');
The advantages of like() over ok() are similar to that of is() and isnt(). Better diagnostics on failure.

unlike

  unlike( this, /that/, testDescription );
Works exactly as like(), only it checks if this does not match the given pattern.

cmpOk

  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:

eq

String equivalence.

ne

String non-equivalence.

lt

String less than.

gt

String greater than.

le

String less than or equal to.

ge

String greater than or equal to.

canOk

  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);
    }
isaOk

  isaOk(object, class, objectName);
Checks to see if the given 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' );
where you'd otherwise have to write

    var obj = new SomeClass();
    ok( defined obj && SomeClass.constructor.prototype.isPrototypeOf(obj));
to safeguard against your test script blowing up.

It works on core classes, too:

    isaOk( [], 'Array' );
The diagnostics of this test normally just refer to 'the object'. If you'd like them to be more specific, you can supply an objectName (for example 'Test customer').

Note that inheritance is detected only for versions of JavaScript that support the isPrototypeOf() method. Earlier versions of JavaScript will only detect an exact match for class membership.

pass

fail

  pass(testDescription);
  fail(testDescription);
Sometimes you just want to say that the tests have passed. Usually the case is you've got some complicated condition that is difficult to wedge into an ok(). In this case, you can simply use pass() (to declare the test ok) or fail (for not ok). They are synonyms for ok(1) and ok(0).

Use these functions very, very, very sparingly.

Diagnostics

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

  diag(message);
  diag(message, message2, message3, ...);
Prints a diagnostic message that is guaranteed not to interfere with test output. All of the arguments to diag() will simply be concatinated together.

Handy for this sort of thing:

    ok( users['foo']), "There's a foo user" )
      || diag("Since there's no foo, check that /etc/bar is set up right");
which would produce:

    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.
All diag()s can be made silent by passing the "no_diag" option to the plan() function: 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.

Comparison functions

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

  isDeeply( got, expected, testDescription );
Similar to is(), except that if 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

  eqArray(got, expected);
Checks if two arrays are equivalent. This is a deep check, so multi-level structures are handled correctly. eqArray() supports numeric arrays and associative arrays, but not objects.

eqAssoc

  eqAssoc(got, expected);
Determines if the two associative arrays contain the same keys and values. This is a deep check. eqAssoc() supports both associative arrays and objects.

eqHash

  eqHash(got, expected);
This function is an alias for eqArray() thrown as a sop to Perl programmers.

eqSet

  eqSet(this, that);
Similar to eqArray(), except the order of the elements is not important. Only values are compared, not the keys (so it makes no difference whether you pass in a numeric array or an associative array). This is a deep check, but the irrelevance of order only applies to the top level.

NOTE: By historical accident, this is not a true set comparison. While the order of elements does not matter, duplicate elements do.

Extending and Embedding TestMore

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:

builder

    var testBuilder = TestMore.builder();
Returns the TestBuilder object underlying TestMore for you to play with.

See Also

TestSimple

If all of these test functions confuse you and you just want to write some simple tests. You can upgrade to TestMore later (it's forward compatible).

http://www.edwardh.com/jsunit/

JSUnit: elaborate xUnit-style testing framework.

Authors

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

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.