Section 8.2.  Test::More

Table of Contents

8.2. Test::More

If you want a little more than the appropriately named Test::Simple, you can move on to the equally appropriately named Test::More.

The first useful thing this module provides is a number of different ways to compare a value against another. First, you can provide two values and ask Test::More if they're the same:

    is( test_function(  ), 1234, "Checking whether test_function returns 1234");

or if they're different:

    isnt( MyClass->new(  ), undef, "new method should succeed");

And you can use regular expressions to see whether something looks like what you expect:

    like(time, qr/^\d+$/, "Time really ought to be a positive integer");

Another useful feature is cmp_ok. It performs explicit numeric, string, or boolean comparisons so you don't have to rely on DWIM. This takes two values and a Perl comparison function, allowing you to specify your tests like this:

    cmp_ok(MyModule::foo(  ), ">", 12, "foo greater than 12");

One advantage of is, isnt, like, and cmp_ok over ok is that they provide more detailed results when a test fails. These can be helpful in debugging a failure:

    not ok 1 - Checking whether test_function returns 1234
    #     Failed test (more.t at line 5)
    #          got: '4321'
    #     expected: '1234'
    not ok 2 - foo greater than 12
    #     Failed test (more.t at line 9)
    #     '12'
    #         >
    #     '12'

The final set of comparison tests deal with comparing structures, something that traditionally has been pretty tedious to do with the ordinary Test and Test::Simplestyles of testing. The is_deeply subroutine compares one structure with another and reports if they're the same and, if not, at what point they vary:

    $got = some_function(  ); # Let's say it returned

                            # [ 1, { a => "foo", b => [ "bar" ] } ]

    $expected =  [1, { a => "foo", b => "bar" }];

    is_deeply($got, $expected);

This example's output is:

    not ok 1
    #     Failed test (t.pl at line 123)
    #     Structures begin differing at:
    #          $got->[1]{b} = 'ARRAY(0x6590)'
    #     $expected->[1]{b} = 'bar'
    # Looks like you failed 1 tests of 1.

showing us that we found an array where we expected a bar scalar.

8.2.1. Skips and Todos

In some cases, you won't want all of your tests to run. There are two major reasons why: first, because the end user's system may not actually have some capability you wish to test; second, you may have written tests for something your code doesn't actually do quite yet. Test::More has the ability to handle both of these cases, which it calls skips and todos, respectively.

Let's take an example. You've written a web services module, and you'd like to test it by connecting to some Internet server and making a query. Unfortunately, not all the world has always-on Internet access yet, so it's polite not to depend on the fact that your tests can make network connections. We'll use the libnet bundle's Net::Config settings to determine whether or not we should make Internet connections during tests:

    use Net::Config qw(%NetConfig);

    my $may_make_connections = $NetConfig{test_hosts};

and if we can't talk to the network, we skip our network-related tests:

    SKIP: {
        skip "No network connection", 2 unless $may_make_connections;


           "Foonly calculator didn't make 2+2 equal 4"

The SKIP: label on the block is mandatory, as it allows Test::More to find the end of the block. The parameters to skip are a string giving the reason why these tests are skipped, and the number of tests to skip. These tests are marked as OK but contain the keyword "skip" in the output so that test harnessesthe frameworks that check the output of test suiteswill know that they haven't actually run.

    ok 1 # skip No network connection
    ok 2 # skip No network connection

The syntax for todo tests is similar, but the outcome is different. Skipped tests output ok and are marked with a skip; todo tests output not ok, but test harnesses will not fail the test suite because they will know that these are todos.

You mark a TODO block by setting the $TODO variable:

    TODO: {
        local $TODO = "Insufficient funds";
        ok(eval { $man->put_on_mars });
        ok(eval { $man->colonize_planet });

When run outside of a testing harness, this will report:

    not ok 2 # TODO Insufficient funds
    #     Failed (TODO) test (t.pl at line 6)
    not ok 3 # TODO Insufficient funds
    #     Failed (TODO) test (t.pl at line 7)

but inside a harness:

    All tests successful.
    Files=1, Tests=3,  0 wallclock secs ( 0.19 cusr +  0.01 csys =  0.20 CPU)

The advantage of this is that as you implement the missing functionality, the tests will gradually begin to pass and the test harness will report them as unexpected successes. Once all the tests pass normally, you can remove the TODO designation.

8.2.2. Automated Tests

As we saw when discussing is_deeply above, Test::More attempts to make it easy to do more complex tests. It also provides a few other features to help automate the testing process.

First, eq_set performs an order-agnostic array comparison. For instance, if you know your function is going to return a list from 1 to 10, but you don't know the order, you can make sure you get a full set of results as follows:

    ok(eq_set([myfunc(  )], [1..10]), "We got a list from 1 to 10");

If you're testing object-oriented modules, Test::More has a few useful additions for you. The isa_ok function checks to see if an object belongs to a particular class; this is typically used to check a constructor:

    my $s = IO::Socket->new;
    isa_ok($s, "IO::Socket");

Finally, there's can_ok, for testing a variety of methods on an object. Strictly speaking, can_ok merely tests the interface to an object, ensuring that it can respond to the methods specified. It calls the can method on the class of the object. If you don't define your own custom version, the universal default can searches the object's inheritance tree for the named method:

    can_ok($s, "accept", "timeout", "connected",
               "close"); # Inherited from IO::Handle

Using these methods together, a great deal of the pain of testing classes can be taken away. Later in the chapter, we'll see how these techniques can be combined with class-based testing to make the creation of such test suites even easier.

    Table of Contents
    © 2000- NIV