NAME
Test::Without - Run code while hiding library paths or specific modules
SYNOPSIS
use Test::Without;
run {
eval "require RPC::XML::Client";
$client = RPC::XML::Client->new();
ok(! $client->compress(), "Client has no compression support");
} without 'Compress::Zlib';
# Run a block with parameters
run {
my %args = @_;
eval "require RPC::XML::Server";
$server = RPC::XML::Server->new(@_);
is($server->port, $args{port}, "Port set correctly");
is($server->path, $args{path}, "Path set correctly");
# Etc.
} without 'Compress::Zlib', 'Net::Server',
params port => 9000, path => '/RPC';
DESCRIPTION
The Test::Without module makes it easy for test scripts to exclude specific modules and/or directories from the Perl search-path during the execution of blocks of code. I wrote this after needing to write a fairly ugly hack for a different CPAN module, in order to test code that would try to load Compress::Zlib, but needed to test the logic paths that only execute when compression is not available. This module is not for testing whether code loads and compiles correctly; see the use_ok
function of Test::More for that.
The module works by creating a lexical scope in which both @INC
and %INC
are localized, and executing the given block within that scope. The modules (and possibly direcories) to be hidden are specified at this time. Directories that are given are immediately removed from @INC
. Modules are handled by means of a subroutine inserted at the head of @INC
.
Conversely, the syntax can be used to require the present of specific modules, throwing an exception via die
if any request resource is not available, or temporarily add extra paths to @INC
. In such a case, none of the code in the provided block will have been run prior to the reporting of the missing resources.
A caller can also provide parameters to be passed to the code block when it is called. This is superfluous for inline-defined blocks, but in cases where the block argument is a code-reference scalar that is being reused, this can be useful.
SYNTAX
The module defines the following functions:
- run BLOCK LIST
-
Run the given code in BLOCK, with the context defined by the elements of LIST. The items in list should be built up using the other functions defined below. Exceptions are not inherently caught, so if you expect the that code may
die
(or otherwise emulate exceptions) you may with to useeval
.If
params
is used (see below) in constructing the context, these values are passed to the code-block in@_
, as though it were a function call. - without LIST
-
Specify a set of resources that should be hidden within the context the associated block is invoked. The contents of LIST should be built up using
modules
(and/orlibs
), below. Because it is expected that the majority of usage will be to mask or require modules, a bare list is assumed to be modules. Thus, the following will work, correctly masking Net::Server from being loadable:run { ... } without 'Net::Server';
- with LIST
-
Specify resources that must be present before the block can be invoked. The given LIST should be built up using a combination of
modules
andlibs
, as needed. Unlike usingwithout
, above, this pre-confirms that modules are available by attempting load them. Directories specified vialibs
are added the same way they would be with the libs pragma, with the added step that a check is first done to confirm the directory actually exists. If it does not exist,die
is called to signal this.Modules specified in a
with
list may provide import-style arguments in a way similar to Perl's -M command-line argument. See the section formodules
, below.As is the case for
without
, above, a bare list is assumed to be modules. The following works as a counter to the previous example:run { ... } with 'Net::Server';
- modules LIST
-
Build a list of modules for use by
without
orwith
. Does no processing of LIST itself.If the modules being specified are for use with the
with
function, then any elements of list may contain parameters using the same specification syntax used for the -M command-line switch of Perl itself:run { # Create an image, then test that it was correct our $image = ...; ($width, $height) = imgsize($image); # Then test to see if we got what was expected } with 'Image::Size=imgsize'; # Requires that Image::Size is present, and imports 'imgsize' # from it.
For syntactic-sugar purposes, you can use the singular
module
as a synonym for this function. - libs LIST
-
Build a list of directories that should be either excluded or required in the Perl search path for the context being constructed. The way these paths are treated depends on whether the list is being used for inclusion or exclusion:
When the list of directories is given to the
without
function, each element is removed from@INC
by calling the unimport method from the lib module. This will also remove architecture-specific sub-directories related to the directory being removed, just as if you invokedno lib $dir
.When the list is given to the
with
function, each element is added to@INC
, along with any related architecture-specific sub-directories, just as if you had invokeduse lib $dir
.
For syntactic-sugar purposes, you can use the singular
lib
as a synonym for this function. - params LIST
-
Build a list of parameters that are passed in as the arguments-list (via
@_
) to the code-block when it is invoked. This can be useful for cases where the code argument is a scalar containing a code-reference that is intended to be reused several times over.For syntactic-sugar purposes, you can use the singular
param
as a synonym for this function.
See the following section for example usage of all the routines defined here.
EXAMPLES
- Test that a class acts correctly in absence of Compress::Zlib:
-
run { require RPC::XML::Client; $client = RPC::XML::Client->new('http://test.com'); ok(! $client->can_compress(), '$client has no compression support'); } without 'Compress::Zlib';
- Semi-emulate the "blib" pragma:
-
run { eval "require Some::Lib;"; ok(Some::Lib->can('some_method'), 'Some::Lib loaded OK'); } with libs 'lib', 'blib', '../lib', '../blib';
- Load code from a local lib while hiding a module:
-
run { ... } with lib 'local', without module 'HTTP::Daemon';
- Run the same code several times, with varying parameters:
-
$code = sub { ... }; $db_credentials = read_all_database_credentials(); for my $db_type (keys %$db_credentials) { my ($user, $pass) = @{$db_credentials->{$db_type}}; # You could say "with params", but it's redundant for params run $code without 'DBD::DB2', params $db_type, $user, $pass; }
DIAGNOSTICS
Any problems are signalled with die. The user must catch these with either the __DIE__
pseudo-signal handler or by eval (or some other syntactic construct).
The code-block that gets inserted into @INC uses die as well, if one of the blocked modules is requested for loading. If your tests are themselves likely to try loading any of these (as opposed to using this framework to hide modules from other code you are loading), you will want to use eval or the signal handler.
CAVEATS
If a module loads that also alters @INC, it could interfere with this module catching and blocking the requests modules or libraries.
BUGS
Please report any bugs or feature requests to bug-test-without at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Without. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
Source code on GitHub
COPYRIGHT & LICENSE
This file and the code within are copyright (c) 2009 by Randy J. Ray.
Copying and distribution are permitted under the terms of the Artistic License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php) or the GNU LGPL 2.1 (http://www.opensource.org/licenses/lgpl-2.1.php).
CREDITS
Thanks to Andy Wardley abw @ cpan.org
for providing the idea for inverting the control-point of the logic and making the scoping issues with @INC and %INC work.
SEE ALSO
Module::Mask, Test::Without::Module, Test::More
AUTHOR
Randy J. Ray <rjray@blackperl.com>