The 2002 Perl Advent Calendar
[about] | [archives] | [contact] | [home]

On the 4th day of Advent my True Language brought to me..
Test::Builder

Okay, I'll admit it, I'm a bit of a testing freak. I like to test my code extensively to make sure it works properly before I ship it.

Last year I talked about using

  • Test::More
  • to test your code, which is a simple library that you can use to do basic tests like checking if something is the same as something else, or if it's an object of a particular class, or many other basic tests. It's a great module since it prints out nice debugging information to show you what output you actually got, and hence why (and where) the test fails. This is almost as important as knowing that your code works - knowing how to fix your code when it's broken greatly speeds up development.

    The trouble simply using Test::More is that often this debugging information isn't enough, or it's too much. Checking individual strings are equal or that simple tests pass doesn't give you enough information, or it prints so much information to your screen that it's hard to see what's going on. What you need to do is code your own little routines that check the data themselves and print out more useful debugging information for the project you're working on.

    This is where Test::Builder comes in. Test::Builder is the underlying library that Test::More uses to produce it's output, and it's cunningly designed so that you can write your own test suites with it that always print out the right kind of debugging information. The best thing about these modules is that they're designed to play nice with other Test::Builder based modules. You can use as many of these modules together as you want and they're work together to ensure that things like the numbers that your tests print don't get screwed up.

    Okay, let's build our own example testing module. In this example we're going to test something pretty useless - if a string equals "Buffy" or not. In a real world example the test would do something much more complicated, but for illustrative purposes, we'll keep it simple.

    We start the testing module just like any other - by declaring a package, turning on strict, and declaring the package variables we're going to use. Note that testing modules traditionally work on very old perls, so it's common for them to use things like use vars instead of using the our keyword for globals to ensure backwards comparability.

      package Acme::Test::Buffy;
      use strict;
      use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      $VERSION = 0.01;

    Now we create a Test::Builder object that we can use in each of the methods in our class. Test::Builder objects are what are known as 'singletons'. This means that there's only one existence ever - each call to new simply returns the same object each and every time. This is how Test::Builder can keep track of numbers for you and emit the right numbers for each test - every module has it's own copy of the same Test::Builder object that it uses for all of it's output.

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

    Since we want to allow people to run the tests with the minimum of fuss we're going to export our function into the caller's namespace. That means that when someone does a use Acme::Test::Buffy in their code that they'll get the ability to is_buffy in script as if they'd written it in place - just like Test::More does with is, ok, etc.

      use Exporter;                   # load the class
      @ISA         = qw(Exporter);    # set it as the base class
      @EXPORT      = qw(is_buffy);    # want to export 'is_buffy'
      @EXPORT_OK   = qw();            # no other optional functions
      %EXPORT_TAGS = qw();            # no groups of functions

    Now all that's left to do is write our is_buffy function itself. The function is designed to be called like so:

      is_buffy($foo, "Is foo buffy?");

    So without further ado:

     sub is_buffy($;$)
     {
       # get the args
       my ($maybe_buffy, $text) = @_;
       # set a default test name
       $text ||= "is buffy";
       # do the test
       if ($maybe_buffy eq "Buffy")
       {
         # print okay with the right text
         $Tester->ok(1,$text);
         return 1; # and return true
       }
       else
       {
         # print not okay with the right text
         $Tester->ok(0,$text);
         # print why it failed
         $Tester->diag("Expected 'Buffy' but got '$maybe_buffy' instead\n");
         return 0; # and return false
       }
     }

    Okay, quite a lot to explain here. First I should point out the function prototype (the ($;$)). This tells Perl how people can call our module - essentially it allows people to be a lot less fussy about having to write brackets. In fact it means that now people can simply write

      is_buffy $foo;

    And it'll still work.

    The real meat of the testing function is the $maybe_buffy eq "Buffy" section. This is a simple test, but this could be as complicated as you want. It decides what functions we should call on our Test::Builder object to produce the output. We either call ok(1,$text) saying that everything is okay and we should print out ok 1 - text, or we call ok(0,$text) to say that everything isn't okay, and that we should print out not ok - 1 text instead, and go onto use the diag function to print out the exact reason the test failed.

    Though it might be tempting to try and print out some of the ok or diagnostic messages ourselves we really shouldn't - as by printing everything out via Test::Builder we ensure the mutual cooperation between all the testing modules, meaning we can do something like

      use Test::More tests => 2;
      use Acme::Test::Buffy;
      # test our re works
      my ($buffy, $angel) = split /:/, "Buffy:Angel";
      is_buffy($buffy);     # an Acme::Test::Buffy test
      is($angel, "Angel");  # a Test::More test

    A Note On Calling Functions

    One of the great things about Test::Builder is that it always reports the line number of the failing test. It is possible to confuse it however, as all it really does is print out where the function that called Test::Builder's ok method was called from. So in this situation

      # simply call the private method instead
      sub is_buffy { _isnt_robot_buffy(@_) }
      # private method not exported from the package
      sub _isnt_robot_buffy
      {
        $Tester->ok(0);
      }

    The diagnostic error for the failing test will be reported inside the is_buffy function as that's the function that called ok, not from the code that called the is_buffy test where it was meant to be. Nasty!

    To solve this problem we simply increase the $Test::Builder::Level variable to indicate we'd like the Test::Builder to look one level higher when reporting errors for the duration of _isnt_robot_buffy. Luckily for us, Perl has a keyword to do just that. local can give a variable a new value that will remain in effect until the end of that block, i.e. till the end of the subroutine, including in all the calls to other routines such as to Test::Builder's ok.

      # simply call the private method instead
      sub is_buffy { isnt_robot_buffy(@_) }
      # private method not exported from the package
      sub _isnt_robot_buffy
      {
        # increase the level
        local $Test::Builder::Level = $Test::Builder::Level + 1;
        $Tester->ok(0);
      }

  • Acme::Test::Buffy full source code
  • Acme::Test::Buffy distribution, including tests
  • Schwern's "Writing A Test Library" Talk
  • Test::Builder::Tester (testing modules built with Test::Builder)