6 Feb 2011

Testing your perl code - Jacinta Richardson - Perltraining.com

==== Testing your code ====

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:

: * Building Testing Libraries
<http://www.perl.com/pub/a/2004/05/07/testing.html>

: * Introduction to Testing
<http://www.perl.com/pub/a/2001/12/04/testing.html?page=1>

: * Perl Testing: A Developer's Notebook; by Ian Langworth and
Chromatic; O'Reilly Media, 20005

2 comments:

  1. With fresh Test::More you can use done_testing instead of no_plan.

    ReplyDelete
  2. @Chorny

    done_testing is added into Test::More-0.87_01 version

    ReplyDelete