The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Test::Group::Extending - writing extensions to Test::Group

WRAPPERS

It's possible to extend Test::Group by writing subroutines that call Test::Group::test(). For example, a replacement for test() that uses the Test::Group function skip_next_test() to skip the test group unless its name appears in an environment variable:

  use Test::Group;

  sub maybe_test ($&) {
      my ($name, $code) = @_;

      my $only = $ENV{ONLY_TEST};
      if ($only and $only !~ /(^|,)\Q$name\E(,|$)/) {
          skip_next_test("$name not enabled");
      }
      goto &test;
  }

In a test script, you can now use maybe_test() anywhere you would use test():

  maybe_test foo => sub {
      ok ...
  };

Note the use of goto &test to pass control to Test::Group::test(), see "goto" in perlfunc. This is good practice when wrapping Test::Group::test(), because it avoids creating a new subroutine call frame and messing up the line numbers in failed test diagnostics.

Sometimes you can't use goto &test, because you want to add some code after the test() call. In this situation you can adjust the line numbers in failed test diagnostics by incrementing $Test::Builder::Level, see Test::Builder.

The following example outputs a timestamp diagnostic before and after running the test group:

  use Test::Builder;
  use Test::Group;
  use Test::More;
  use Time::HiRes;

  sub timed_test ($&) {
      my ($name, $code) = @_;

      diag("$name start: ".Time::HiRes::time());

      local $Test::Builder::Level = $Test::Builder::Level + 1;
      &test($name, $code);

      diag("$name done:  ".Time::HiRes::time());
  };

Note the use of the ampersand in the call to Test::Group::test(). This bypasses Test::Group::test()'s function prototype, which would otherwise reject $code as the second parameter because it is not a literal code block.

PLUGINS

From version 0.16 onwards, Test::Group provides the function next_test_plugin() (not exported by default) to install a plugin for the next test group. A plugin is a subroutine that sits in between Test::Group::test() and the subroutine reference passed to it.

The next_test_plugin() function takes a single parameter, which must be a subroutine reference. That subroutine will be called with a single parameter: another subroutine reference which will run the test group.

For example, a plugin to check that a test group does not modify the PATH environment variable could be implemented like this:

  use Test::Group qw(:DEFAULT next_test_plugin);
  use Test::More;

  sub next_test_nopathchange {
      next_test_plugin {
          my $next = shift;

          my $old = $ENV{PATH};
          $next->();
          is $ENV{PATH}, $old, "path not modified";
      };
  }

To use this plugin from a test script:

  next_test_nopathchange();

  test foo => sub {
      do_my_tests('foo');
  };

Another example - the following plugin runs the test group twice, with and without a DEBUG environment variable set:

  use Test::Group qw(:DEFAULT next_test_plugin);

  sub next_test_with_and_without_debug {
      next_test_plugin {
          my $next = shift;

          $next->();
          local $ENV{DEBUG} = 1;
          $next->();
      };
  }

In a test script, you might apply both plugins to the same test group:

  next_test_with_and_without_debug();
  next_test_nopathchange();

  test foo => sub {
      do_my_tests('foo');
  };

When multiple plugins have been set, the one that was set first gets control first. In the example above, the DEBUG plugin will be called first, and each time it calls $next->() control passes to the PATH plugin. When the PATH plugin calls $next->(), control gets down to do_my_tests().

If you want to apply the same set of plugins to several groups in your test script, then you can write a Test::Group::test() wrapper to set them up:

  sub mytest ($&) {
      next_test_with_and_without_debug();
      next_test_nopathchange();

      goto &test;
  }

  mytest foo => sub {
      do_my_tests('foo');
  };

  mytest bar => sub {
      do_my_tests('bar');
  };

SEE ALSO

Test::Group

Test::Group::Tester helps you to write tests for Test::Group extensions.