Writing our own tests with Test::More

[ Perl tips index ]
[ Subscribe to Perl tips ]

Test::More provides many great testing functions. We'll use some of these throughout this tip, but you should read the documentation for more information.

Basic tests (ok)

The most simple test of all, is do we have a true value, and we test that with ok():

        use Test::More tests => 2;

        ok(1);  # This test will pass
        ok(0);  # This test will fail

We can also name our tests, as the optional last argument:

        use Test::More tests => 2;

        ok(1, "1 is true");
        ok(0, "0 is not true");

This very simple test is great for testing all sorts of things, just like we would in a conditional:

        use Test::More tests => 4;

        ok($foo,   '$foo is true');
        ok(@array, '@array has values in it');
        ok(%hash,  '%hash has values in it');
        ok(bar(),  'bar() returned a true value');

Comparative tests

We can use ok with comparisons, but it doesn't work as well as we'd like:

        use Test::More tests => 2;

        ok( sum(1..10) == 55, "sum() works properly");
        ok( product(1..10) == 3_628_880, "product() works properly");

When we run just these tests we get:

        ok 1 - sum() works properly
        not ok 2 - product() works properly
        #   Failed test 'product() works properly'
        #   at ok.t line 10.
        # Looks like you failed 1 test of 2.

But other than the fact that the second result was not true, we can't see why not. It's all very well to know whether something is true or not, but sometimes we'd prefer to compare the value to another value. Rather than ask did this return a true value, we might prefer to ask did it return the number 5?

We can do that with the test is(). is() is true if the first argument equals the second.

        use Test::More tests => 2;

        is( sum(1..10), 55, "sum() works properly");
        is( product(1..10), 3_628_880, "product() works properly");

When we run these test we get:

        1..2
        ok 1 - sum() works properly
        not ok 2 - product() works properly
        #   Failed test 'product() works properly'
        #   at ok.t line 10.
        #          got: '3628800'
        #     expected: '3628880'
        # Looks like you failed 1 test of 2.

Now we get to compare. Here we can see that there's an error in our test. We should have written:

        is( product(1..10), 3_628_800, "product() works properly");

(that extra 8 can be hard to spot).

Having a plan

In the previous examples we told Test::More how many tests we were going to be running. We do this to guard against premature failure. If we tell Test::More how many tests we expect to run, then we set the plan up front:

        use Test::More tests => 2;

If we don't know how many tests we're going to run we can just tell Test::More when we've finished testing:

        use Test::More;

        # tests

        done_testing();

We can also set our plan once our test script is in action:

        use Test::More;

        # work out how many tests we want to run somehow...

        plan tests => $number_of_tests;

        # now do tests

or decide that we want to skip these tests based on some condition:

        use Test::More;

        if( $^0 eq "Win32" ) {
                plan skip_all => "Test not relevant for Windows 32 platform";
        }
        else {
                plan tests => 5;
        }

        # now do tests

When we skip_all tests, your script will output the reason why the tests are being skipped and then exit immediately with a zero (for success).

SKIP blocks

If instead of wanting to skip all the tests, we wish to only skip some, we can use a SKIP block:

        SKIP: {
                skip $why, $how_many if $condition;
        }

The condition is optional, but a good idea.

This might be useful if we're changing our code, and these tests might no longer apply, or if some of our tests depend on a module which hasn't been installed, or for all sorts of other reasons. The code inside a SKIP block will not be run at all.

We must specify how many tests we're skipping ($howmany) if we've set a plan so that the end count of tests ends up matching what Test::More expects.

TODO blocks

If we have tests which fail, or which test unfinished code, we can use a TODO block. These are great because the tests are still run, but we expect them to fail so failures are okay.

        TODO: {
                local $TODO = $why if $condition;

        }

The condition is optional, and isn't often included.

Because the tests are still run, Test::More can flag tests when they succeed as unexpected successes. This means that you can write a bunch of tests for functionality that you don't yet have, and then as you add that functionality your tests will start being useful. Once you're happy with the results you can move the working tests outside the TODO block.

[ Perl tips index ]
[ Subscribe to Perl tips ]


This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Email: contact@perltraining.com.au
Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS