Testing your code

[ Perl tips index ]
[ Subscribe to Perl tips ]

Testing cannot prove the absence of bugs, although it can help identify them for elimination. It's impossible to write enough tests to prove that your program is flawless. However, a comprehensive test-plan with an appropriate arsenal of tests can assist you in your goal of making your program defect free.

Test::Simple

Writing a test is just like writing regular Perl code, and checking that what you got is what you expected. There are a huge number of modules in Perl which are designed to make testing that much easier. These are all built on Test::Harness underneath. Test::Simple comes standard with Perl and provides a great starting point.

Test::Simple provides one test routine: ok. You pass it an expression, and a description. If the expression, when evaluated, is true, the test succeeds, otherwise it fails. For example:

        use Test::Simple tests => 1;

        ok( 1 + 1 == 2, 'simple arithmetic works');

If we save this into a file, and run that file, we get:

        1..1
        ok 1 - simple arithmetic works

We can add a failing test too:

        use Test::Simple tests => 2;

        ok( 1 + 1 == 2, 'simple arithmetic works');
        ok( 1 + 3 == 2, 'of course, this should fail');

and then we get:

        $ perl testscript.pl
        1..2
        ok 1 - simple arithmetic works
        not ok 2 - of course, this should fail
        #   Failed test 'of course, this should fail'
        #   at test.pl line 4.
        # Looks like you failed 1 test of 2.

So let's consider this code. First of all we must have a plan. This tells Test::Simple how many tests we intend to run. If we get this number wrong, our test suite will not pass; as Test::Simple cannot tell whether we made a mistake in our plan, or if some tests didn't run for some other reason.

Note that we didn't need to specify test numbers, just how many tests we have. Test::Simple will keep track of the test numbers for us.

Test::More

Test::More provides a richer set of functions to make your tests even easier to write. For example, is, isnt, like and unlike:

        use Test::More tests => 8;

        # Load in my module, fail if it won't load.
        use_ok 'Local::People';

        # Test if simple addition is working.
        is( (1 + 5), 6, "Simple addition behaves");

        # Let's not compare apples to oranges.
        isnt( "Malus domestica", 
              "Citrus sinensis", 
              "Inequal string comparison" 
        );

        # Test that we can create a new person
        my $self;
        eval { $self = Local::People->new(cache_id => 1234) };
        like( $@, undef, 'Correctly created $self' );

        # Does my name match this regular expression?
        like ( $self->name(), qr/Jacinta/, 'Name contains Jacinta' );

        # We can also provide a regular expression in a string form:
        like ( $self->hobby(), '/Perl/i', 'Name contains Perl' );

        # Make sure my favourite foods don't contain broad beans.
        unlike ( $self->favourite_foods(), 
                 qr/broad beans/i, 
                 "Don't like icky foods" 
        );

        # Test that languages matches the expected list:
        my @languages = qw/Perl C PHP C++ Bash/;
        is_deeply( [sort $self->languages()], 
                   [sort @languages], 
                   'All the languages are there' 
        );

Having a plan

With Test::More, if you're just starting your test suite, or adding an unknown number of extra tests you can tell Test::More that you don't know how many tests there will be in total:

        use Test::More qw(no_plan);

Of course, once your test suite has become more stable, you should always provide a number of tests. This allows your testing modules to tell if your tests have stopped prematurely (which usually indicates something has gone wrong).

You can also delay defining your plan until later:

        use Test::More;

        # ...

        plan( tests => 3 );

This is useful if you won't know how many tests you expect to run until some condition is decided at run-time. For example, if you have some tests which depend on an environment variable being set, or a module being installed. It's a mistake to set a plan, and then change it later.

ok() vs is()

If you make an error either in your module, or in your test script ok() will only tell you that your test failed. is() is a lot friendlier and will tell you both what you got, and what you said you were expecting. For example ok() might give us:

        $ perl -I lib/ testprogram.pl
        1..2
        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.

Which doesn't necessarily help us spot what we've done wrong. On the other hand, if we change our tests to use is() we'll get:

        $ perl -I lib/ testprogram.pl
        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.

Here we can see that our error is in the number we said we're expecting (we've mistyped it).

Further resources

There's a whole lot more to testing, and Test::More does a lot more than we've covered here. Many articles on testing in Perl and specifically Test::More have been written, and we recommend these in particular:

[ 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