NAME
Moan - yet another "assert" module
SYNOPSIS
In lib/MyApp/Database.pm
:
package MyApp::Database;
use strict;
use Moan;
sub new {
my ($class, $dbh) = @_;
assert($dbh->isa('DBI::db'), 'Passed a database.');
assert($dbh->ping, 'Database is alive.');
bless [$dbh], $class;
}
sub dbh {
$_[0][0];
}
1;
In bin/myapp.pl
:
#!/usr/bin/perl
use MyApp::Database;
use Moan;
assert(defined $ENV{MYAPP_DB}, 'MYAPP_DB set');
my $db = MyApp::Database->new(DBI->connect($ENV{MYAPP_DB}));
assert($db->isa('MyApp::Database'), 'Class OK');
# do something
At the command-line:
perl -Moan::more=MyApp::Database bin/myapp.pl
DESCRIPTION
Assertions are somewhere between inlined unit tests, checks against malicious input and executable comments. Assertions should be written to have no side-effects, and to always pass under normal circumstances.
This is not the only Perl assertion module, and probably not the best, but it does what I need. This module requires Perl 5.10.
Assertion Functions
assert($expression, $message, $tag)
-
Checks whether
$expression
evaluates to true. If not, the assertion fails (see "Assertion Consequences").The second parameter
$message
is optional, and provides an error message to associate with the assertion. If omitted or undef, a default non-descriptive message is used.The third parameter
$tag
is a category for the assertion. This allows you to classify assertions and disable them by tag. If ommitted or undef, the default tag is the package name where the assertion occurred. The following two assertions are equivalent:package Foo::Bar; assert(1); package main; assert(1, undef, 'Foo::Bar');
Tags starting with a single colon are appended to the package name. So the following two assertions are equivalent:
package Foo::Bar; assert(1, undef, ':quux'); package main; assert(1, undef, 'Foo::Bar:quux');
This means you have very little excuse for not namespacing your tags. For small packages, just leave the tag undefined and you'll get your assertion tagged with your package. For larger packages, use tags matching the expression
/^:(\w+)$/
.Tags are in fact arbitrary strings, but you can save yourself headaches if you stick to the conventions above.
affirm {CODE_BLOCK} $message, $tag
-
Checks that the return value of the code is true. Good for more complex tests. (This is also more efficient than
assert
in cases where you've disabled assertion reporting, asaffirm
can avoid running the code block entirely. diag $message, $tag
-
Strictly speaking, this does not actually make an assertion, but generates a diagnostic message (which is not usually shown).
The "Assertion Consequences" section explains how to enable diagnostic messages on a per-tag basis.
no Moan
-
Disables moaning within a lexical block.
Carp::Assert
These functions are provided for rough compatibility with Carp::Assert. You should be able to more or less drop in Moan
as a replacement:
use Moan ':carpish';
should($given, $expected, $message, $tag)
-
Equivalent to:
assert($given ~~ $expected, $message, $tag);
This is slightly different from
Carp::Assert::should
which useseq
instead of the smart match. shouldnt($given, $expected, $message, $tag)
-
Equivalent to:
assert(!($given ~~ $expected), $message, $tag);
DEBUG
-
Right now this just returns 1.
Test::More
These functions are provided for rough compatibility with Test::More. (Though note that Moan
is not designed for writing module tests. It doesn't use TAP.) They are not exported by default.
use Moan ':testmore';
ok($expression, $message, $tag)
-
Largely the same as
assert
, provided for people addicted to Test::More. is($given, $expected, $message, $tag)
-
Equivalent to:
ok($given eq $expected, $message, $tag);
or
ok($given == $expected, $message, $tag);
... depending on whether
$expected
looks like a number. isnt($given, $expected, $message, $tag)
-
Equivalent to:
ok($given ne $expected, $message, $tag);
or
ok($given != $expected, $message, $tag);
... depending on whether
$expected
looks like a number. like($given, $expected, $message, $tag)
-
Equivalent to:
ok($given =~ $expected, $message, $tag);
unlike($given, $expected, $message, $tag)
-
Equivalent to:
ok($given !~ $expected, $message, $tag);
isa_ok($package_name, $class, $message, $tag)
isa_ok($object, $class, $message, $tag)
-
Roughly equivalent to:
ok($package_name->isa($class), $message, $tag); ok($object->isa($class), $message, $tag);
... but without failing horribly if $object is, say, an unblessed reference.
can_ok($package_name, $method, $message, $tag)
can_ok($object, $method, $message, $tag)
-
Roughly equivalent to:
ok($package_name->can($method), $message, $tag); ok($object->can($method), $message, $tag);
... but without failing horribly if $object is, say, an unblessed reference.
Also Useful
Here are some other functions that are occasionally useful.
use Moan qw/:standard :testmore does_ok defined_ok blessed_ok/;
does_ok($package_name, $role, $message, $tag)
does_ok($object, $role, $message, $tag)
-
Roughly equivalent to:
ok($package_name->DOES($role), $message, $tag); ok($object->DOES($role), $message, $tag);
... but without failing horribly if $object is, say, an unblessed reference.
defined_ok($value, $message, $tag)
-
Pretty much identical to:
ok(defined $value, $message, $tag);
blessed_ok($object, $message, $tag)
-
ok(Scalar::Util::blessed($object), $message, $tag);
Export
%EXPORT_TAGS = (
testmore => [qw/ok is isnt like unlike can_ok isa_ok/],
carpish => [qw/assert affirm should shouldnt DEBUG/],
standard => [qw/assert affirm diag/],
);
@EXPORT = @{ $EXPORT_TAGS{standard} };
@EXPORT_OK = (
qw/blessed_ok defined_ok does_ok TRUE FALSE/,
@{ $EXPORT_TAGS{standard} },
@{ $EXPORT_TAGS{testmore} },
@{ $EXPORT_TAGS{carpish} },
);
Yeah, you can export the constants TRUE and FALSE, just because they're so often handy to have.
Assertion Consequences
The normal consequence of an assertion is that a warning will be emitted, if the assertion does not hold (i.e. is false). Diagnostic messages (diag
) will not be printed.
Moan
can be kicked into a more verbose mode, where diagnostic messages are printed, and assertions that don't hold are accompanied by stacktracers. Just call Moan->loudly
Moan->loudly(qw/MyApp::Database YourApp::Greaterface/);
Each argument is a tag which you want to make "louder". The /^:\w+$/
convention for tags that are local to your package will work, but to specify tags in other packages, you need to fully-qualify the names.
For example, the tag ":connection" in package MyApp::Database could be made louder using:
Moan->loudly('MyApp::Database:connection'); # sic
Tags can be preceded with a minus to switch off the loudness feature.
Regular expressions also work:
Moan->loudly(qr/^MyApp::Database:(\w+)$/);
There's currently no way to remove regular expressions from the list though, so use with caution.
A special tag "ALL" makes all moaning louder. "-ALL" counteracts that.
But what if you want to decrease Moan
's verbosity? As you might have guessed, Moan->quietly
fulfils this role. It takes the same parameters as Moan->loudly
.
If a tag has been quietened, then no warnings will be emitted for it whether the assertion holds or not. In fact, in many cases Moan
can even avoid completely evaluating the assertion.
Curiously, a tag can be made both loud and quiet, as the two features are quite separate. If a tag is both, then it will act quiet, except that warnings for diagnostic messages will be emitted. This should be considered an "at risk feature" (i.e. an arguable bug).
There is also a Moan->fatally
feature. This again takes the same parameters as Moan->loudly
. It makes failed assertions throw an exception (i.e. die).
When Moan
needs to throw an exception for an assertion, it checks to see if the tag is loud, and if not, appends all previous diagnostics to the exception.
At some point, the exceptions thrown will probably be switched from strings to blessed objects in the next release.
(Moan::loudly
, Moan::quietly
and Moan::fatally
also work.)
The following functions are provided to check the behaviour of a tag:
Command-Line Options
perl -Moan::quietly=tag1,tag2,tag3
- quietlyperl -Moan::loudly=tag1,tag2,tag3
- loudlyperl -d:oh=tag1,tag2,tag3
- loudly and fatally
BUGS
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=Moan.
SEE ALSO
Carp::Assert
, Test::More
.
AUTHOR
Toby Inkster <tobyink@cpan.org>.
COPYRIGHT AND LICENCE
This software is copyright (c) 2011 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.