Приглашаем посетить
Некрасов (nekrasov-lit.ru)

Section 17.5.  Writing Tests with Test::More

Previous
Table of Contents
Next

17.5. Writing Tests with Test::More

The Test::More[*] module comes with the standard Perl distribution starting with Perl 5.8, and it's available on CPAN if we need to install it on earlier Perl versions. It works all the way back to perl5.6, at least. We rewrite the tests from the previous section (and throw in a couple more) in this test script, which uses the ok( ) function from Test::More.

[*] The name plays off Test::Simple, which did some of the same stuff but with far fewer convenience functions.

use Test::More tests => 4;

ok(1 + 2 =  = 3, '1 + 2 =  = 3');
ok(2 * 4 =  = 8, '2 * 4 =  = 8');
my $divide = 5 / 3;
ok(abs($divide - 1.666667) < 0.001, '5 / 3 =  = (approx) 1.666667');
my $subtract = -3 + 3;
ok(($subtract eq '0' or $subtract eq '-0'), '-3 + 3 =  = 0');

The ok( ) function prints "ok" if its first argument is true, and "not ok" otherwise. The optional second argument lets us name the test. After the initial output to indicate success, ok( ) adds a hyphen and then the name of the test. When we find a test that does not work, we can find it by the name that we gave it.

1..4
ok 1 - 1 + 2 =  = 3
ok 2 - 2 * 4 =  = 8
ok 3 - 5 / 3 =  = (approx) 1.666667
ok 4 - -3 + 3 =  = 0

Now Test::More does all of the hard work. We don't have to think about the output or the test numbering. But what about that nasty little 4 constant in the first line? That's fine once we're shipping the code, but while we're testing, retesting (retesting some more), and adding more tests, we don't want to keep updating that number to keep the test harness from complaining about too many or too few tests. We change that to no_plan so Test::More can figure it out later.

use Test::More "no_plan";        # during development

ok(1 + 2 =  = 3, '1 + 2 =  = 3');
ok(2 * 4 =  = 8, '2 * 4 =  = 8');
my $divide = 5 / 3;
ok(abs($divide - 1.666667) < 0.001, '5 / 3 =  = (approx) 1.666667');
my $subtract = -3 + 3;
ok(($subtract eq '0' or $subtract eq '-0'), '-3 + 3 =  = 0');

The output from Test::More is a bit different, and the test range moves to the end. That's just the number of tests that we actually ran, which might not be the number we intended to run:

ok 1 - 1 + 2 =  = 3
ok 2 - 2 * 4 =  = 8
ok 3 - 5 / 3 =  = (approx) 1.666667
ok 4 - -3 + 3 =  = 0
1..4

The test harness knows that if it doesn't see a header, it's expecting a footer. If the number of tests disagree or there's no footer (and no header), it's a broken result. We can use this during development, but we have to remember to put the final number of tests in the script before we ship it as real code.

But wait: there's more (to Test::More). Instead of a simple yes/no, we can ask if two values are the same. The is( ) function, as we showed earlier, compares its first argument, which is the result we got, with the second argument, which is what we expected. The optional third argument is the name we give the test, just as we did with the ok function earlier.

use Test::More 'no_plan';

is(1 + 2, 3, '1 + 2 is 3');
is(2 * 4, 8, '2 * 4 is 8');

Note that we've gotten rid of numeric equality and instead asked if "this is that." On a successful test, this doesn't give much advantage, but on a failed test, we get much more interesting output.

use Test::More 'no_plan';

is(1 + 2, 3, '1 + 2 is 3');
is(2 * 4, 6, '2 * 4 is 6');

This script yields much more useful output that tells us what is( ) was expecting and what it actually got.

ok 1 - 1 + 2 is 3
not ok 2 - 2 * 4 is 6
#     Failed test (1.t at line 4)
#          got: '8'
#     expected: '6'
1..2
# Looks like you failed 1 tests of 2.

Of course, this is an error in the test, but note that the output told us what happened: we got an 8 when we were expecting a 6.[*] This is far better than just "something went wrong" as before. There's also a corresponding isnt( ) when we want to compare for inequality rather than equality.

[*] More precisely: we got an '8' but were expecting a '6'. Did you notice that these are strings? The is( ) test checks for string equality. If we don't want that, we just build an ok( ) test instead, or try cmp_ok, coming up in a moment.

What about that third test, where we allowed the value to vary within some tolerance? Well, we can just use the cmp_ok routine instead.[*] The first and third arguments are the operands,[Section 17.5.  Writing Tests with Test::More] and the intervening, second argument is the comparison operator (as a string!).

[*] Although Test::Number::Delta can handle this for us.

[Section 17.5.  Writing Tests with Test::More] There's no joy for RPN fans.

use Test::More 'no_plan';

my $divide = 5 / 3;
cmp_ok(abs($divide - 1.666667), '<' , 0.001,
      '5 / 3 should be (approx) 1.666667');

If the test we give in the second argument fails between the first and third arguments, then we get a descriptive error message with both of the values and the comparison, rather than a simple pass or fail value as before.

How about that last test? We wanted to see if the result was a 0 or minus 0 (on the rare systems that give back a minus 0). We can do that with the like( ) function:

use Test::More 'no_plan';

my $subtract = -3 + 3;
like($subtract, qr/^-?0$/, '-3 + 3 =  = 0');

Here, we take the string form of the first argument and attempt to match it against the second argument. The second argument is typically a regular expression object (created here with qr), but we can also use a simple string, which like( ) will convert to a regular expression object. We can even write the string form as if it were (almost) a regular expression:

like($subtract, qr/^-?0$/, '-3 + 3 =  = 0');

The string form is portable back to older Perls.[Section 17.5.  Writing Tests with Test::More] If the match succeeds, it's a good test. If not, the original string and the regex are reported along with the test failure. We can change like to unlike if we expect the match to fail instead.

[Section 17.5.  Writing Tests with Test::More] The qr// form wasn't introduced until Perl 5.005.


Previous
Table of Contents
Next