2014 twenty-four merry days of Perl Feed

Optional tests for optional requirements

Test::Requires - 2014-12-17

There are some modules on CPAN that provide optional features, where the availability of the feature is dependent on one or more other modules. One common example is optional support for export of data as an SQLite database, which is only supported if DBD::SQLite is installed.

Of course you want tests for all features of your module, and that means you need an optional test for your optional feature. And that's where Test::Requires comes in.

Let's say you've got some module that builds up information from the net, then can export it in a number of formats. There might be some core formats, like JSON and XML, but there might be some optional ones, such as SQLite. So in your test directory, you might have a file `export-as-sqlite.t`, which would start with:

use Test::Requires qw/ DBD::SQLite /;
# write test that can safely assume that DBD::SQLite is available

If DBD::SQLite isn't available, then the entire test-suite will be skipped. Unless you're running release tests (for example when running `dzil release`), in which case the test-suite will bail out.

You can also use Test::Requires to require a particular version of Perl for a specific test-suite:

use Test::Requires '5.010';

How do people use it in real life?

Sometimes you discover a new module that seems pretty groovy, but you can't imagine a use case where you might actually use it. A handy tip for such situations: use grep.cpan.me to find out how other people use the module. In the search box just enter:

use Test::Requires

And you'll get a paged list of how existing modules use Test::Requires. Let's look at some real-life examples for Test::Requires!

BPM::Engine

BPM::Engine is a "Business Process Execution Engine", which can use various database back-ends. It has a class t::TestUtils used by all of the tests, and in there you'll see the line:

use Test::Requires 'DBD::SQLite';

There's an additional small point illustrated here. To run this test you also need DBI installed, but given that DBD::SQLite depends on DBI, you are essentially covering both with the above statement. In general, if there's a chain of dependencies, you only need to specify the last module in the chain.

namespace::autoclean

namespace::autoclean is used to ensure that symbols imported into your module don't pollute your namespace. It has a test file moo.t, which tests usage of namespace::autoclean with Moo. At the start of this file you'll see the following, which means that Moo isn't required to install namespace::autoclean

use strict;
use warnings;
use Test::More 0.88;
{
  package Temp1;
  use Test::Requires {
    'Moo' => 0,
  };
}

Why this funny-looking construct? Unfortunately use Test::Requires 'Foo::Bar' ends up running eval "use Foo::Bar", which means that if Foo::Bar exports any symbols by default, then they'll end up in your namespace. Putting this in a sacrificial namespace avoids that problem.

Kavorka

Toby Inkster's Kavorka provides function and method declaration keywords that support signatures. Typically for Toby, he's made sure it plays nicely with Moose, Mouse, and Moo. So in the test-suite 21modifiers-moose.t you'll see:

 use Test::Requires { 'Moose' => '2.0000' };

This shows the syntax for specifying a minimum required version of the module.

ZeroMQ

Above I said that you only need to use Test::Requires on the last module in a dependency chain. But sometimes a test might require two independent modules. For example, ZeroMQ's 006_anyevent.t has the line:

use Test::Requires qw( Test::TCP AnyEvent );

Which means the test will be skipped unless both modules are installed.

Tiffany

TOKUHIROM's Tiffany provides a generic interface to multiple templating engines. So by now you shouldn't be surprised to see the following line in a test:

use Test::Requires 'Text::Xslate';

Text::Xslate

You really don't want memory leaks in your templating system, but not everyone will have modules like Test::LeakTrace installed. So Text::Xslate's test file 200_leaktrace.t has:

use Test::Requires { 'Test::LeakTrace' => 0.13 };

Similarly other modules have optional tests using Devel::Monitor.

Summary

Test::Requires is handy when writing tests for a distribution that provides optional features based on modules in other CPAN distributions. Some common examples are:

  • Supporting multiple database engines.

  • Supporting multiple template engines.

  • Where some things are only accessible via https, so you need IO::Socket::SSL.

  • Supporting multiple serialisation modules, such as Storable, Sereal, or even SQLite.

  • Supporting multiple formats like YAML, JSON, XML.

  • Supporting multiple HTTP request user agents, such as HTTP::Tiny, LWP, WWW::Mechanize.

  • Running some tests only if certain Devel:: modules are installed.

One thing to be aware of: Test::Requires ends up use'ing the target modules.

See Also

Gravatar Image This article contributed by: Neil Bowers <neil@bowers.com>