Приглашаем посетить
Есенин (sergeiesenin.lit-info.ru)

Section 18.7.  Writing Your Own Test::* Modules

Previous
Table of Contents
Next

18.7. Writing Your Own Test::* Modules

You don't have to wait for other people to write cool test modules. If you have a particular testing situation that you'd like to wrap up in a test function, you can write your own Test::* module using the Test::Builder module, which handles all of the tricky integration with Test::Harness and Test::More. If you look behind the scenes of many of the Test::* modules, you'll find Test::Builder.

Again, the advantage to test functions is that they wrap reusable code in a function name. To test something, you use the function name rather than typing out a bunch of separate statements. It's easy for people to understand what you meant to test based on a single function name, but that gets harder as you write out several statements to do the same thing.

In Chapter 4, we wrote some code to check that the castaways had all of their required items. Let's turn that into a Test::* module. Here's the check_required_items subroutine as we left it:

sub check_required_items {
  my $who   = shift;
  my $items = shift;

  my @required = qw(preserver sunscreen water_bottle jacket);
  my @missing = (  );

  for my $item (@required) {
    unless (grep $item eq $_, @$items) { # not found in list?
      print "$who is missing $item.\n";
      push @missing, $item;
    }
  }

  if (@missing) {
    print "Adding @missing to @$items for $who.\n";
    push @$items, @missing;
  }
}

We need to turn this into a Test::* module that simply checks the items (so it doesn't add the missing ones) and then outputs the right thing. The basics for any new testing module are the same. We call our new module Test::Minnow::RequiredItems and start with this stub:

package Test::Minnow::RequiredItems;
use strict;

use base qw(Exporter);
use vars qw(@EXPORT $VERSION);

use Test::Builder;

my $Test = Test::Builder->new(  );

$VERSION = '0.10';
@EXPORT  = qw(check_required_items_ok);

sub check_required_items_ok {
        # ....
        }

1;

We start by declaring the package, then turning on strictures because we want to be good programmers (even if this three-hour tour is ultimately doomed, it won't be from one of our software errors). We pull in the Exporter module and add required_items_ok to @EXPORT, since we want that function to show in the calling namespace, just as we discussed in Chapter 15. We set $VERSION just like we discussed in Chapter 16. The only stuff we haven't shown you is Test::Builder. At the beginning of our test module, we create a new Test::Builder object that we assign to the lexical variable $Test, which is scoped to the entire file.[*]

[*] It's almost like a global variable, except it doesn't live in a package and can't be seen outside its file.

The $Test object is going to handle all of the testing details for us. We remove all of the output parts from check_required_items, and we take out the parts to modify the input list. Once we go through the other logic, the only thing we need to do at the end is tell the test harness if the test is ok or not_ok.

sub check_required_items {
  my $who   = shift;
  my $items = shift;

  my @required = qw(preserver sunscreen water_bottle jacket);
  my @missing = (  );

  for my $item (@required) {
    unless (grep $item eq $_, @$items) { # not found in list?
      push @missing, $item;
    }
  }

  if (@missing) {
  ...
  }
  else {
  ...
  }
}

Now we have to add the parts to turn our function into a testing one. We call methods on $Test to tell the test harness what happened. In each case, the last evaluated expression should be a call to $Test->ok( ), so that becomes the return value of the entire function.[*] If we discovered missing items, we want the test to fail, so we pass a false value to $Test->ok( ), but before we do that we use $Test->diag( ) with a message to tell us what went wrong.

[*] We often don't use the return value since most people call most test functions in a void context, but we might as well return something that makes sense.

sub check_required_items_ok {
  my $who   = shift;
  my $items = shift;

  my @required = qw(preserver sunscreen water_bottle jacket);
  my @missing = (  );

  for my $item (@required) {
    unless (grep $item eq $_, @$items) { # not found in list?
      push @missing, $item;
    }
  }

  if (@missing) {
    $Test->diag( "$who needs @missing.\n" );
    $Test->ok(0);
  }
  else {
    $Test->ok(1);
    }
}

That's it. Although there are more things that we can do, there isn't more that we have to do. Once we save our Test::Minnow::RequiredItems, we can use it immediately in a test script. We still use Test::More to set the plan.[*]

[*] We could do that from our module, but most likely the test script will use other modules too. Only one of them can set the plan, so we let Test::More handle that.

use Test::More 'no_plan';
use Test::Minnow::RequiredItems;

my @gilligan = (
        Gilligan => [ qw(red_shirt hat lucky_socks water_bottle) ]
        );

check_required_items_ok( @gilligan );

Since Gilligan doesn't have all of his required items, the test fails. It prints the not_ok along with the diagnostic message.

not ok 1
1..1
# Gilligan needs preserver sunscreen jacket.
#     Failed test (/Users/Ginger/Desktop/package_test.pl at line 49)
# Looks like you failed 1 test of 1.

And, now that we've created the Test::Minnow::RequiredItems module, how we do we test the test? We can use the Test::Builder::Tester module. You'll have to investigate that one yourself, though.


Previous
Table of Contents
Next