Why not adopt me?
NAME
Generic::Assertions - A Generic Assertion checking class
VERSION
version 0.001002
ALPHA
This is pre-release code, and as such API
is very much subject to change.
Best attempts at being consolidated is already made, but there's no guarantees at this time things won't change and break API
without warning.
SYNOPSIS
use Generic::Assertions;
use Path::Tiny qw(path);
my $assert = Generic::Assertions->new(
exist => sub {
return (1, "Path $_[0] exists") if path($_[0])->exists;
return (0, "Path $_[0] does not exist");
},
);
...
sub foo {
my ( $path ) = @_;
# carp unless $path exists with "Path $path does not exist"
$assert->should( exist => $path );
# carp if $path exists with "Path $path exists"
$assert->should_not( exist => $path );
# croak unless $path exists with "Path $path does not exist"
$assert->must( exist => $path );
# Lower level way to use the assertion simply to return truth value
# without side effects.
if ( $assert->test( exist => $path ) ) {
}
# carp unconditionally showing the test result and its message
$assert->log( exist => $path );
}
DESCRIPTION
Generic::Assertions
allows you to create portable containers of classes of assertions, and allows keeping severity of assertions from their implementation.
Basic implementation entails
Defining a list of things to test for
Returning a pair of ( OK / NOT_OK , "reason" ) for the tests conclusion
[optional] Defining a default handler for various classes of severity (
should
,must
etc. )[optional] Defining an input transform (eg: always converting the first argument to a path)
Invoking the assertion at the callpoint as
$instance->severity_level( test_name => @args_for_test )
METHODS
new
Constructs a Generic::Assertions object.
my $assertion = Generic::Assertions->new( ARGS );
The following forms of ARGS
is supported:
->new( key => value );
->new({ key => value });
All keys
without a -
prefix are assumed to be test names, and are equivalent to:
->new( -tests => { key => value } );
-tests
All tests must have a simple string key, and a CodeRef
value.
An example test looks like:
sub {
my ( @slurpy ) = @_;
if ( -e $slurpy[0] ) {
return ( 1, "$slurpy[0] exists" );
}
return ( 0, "$slurpy[1] does not exist" );
}
That is, each test must return either a true
value or a false
value. And each test must return a string describing the condition.
This is so it composes nicely:
$ass->should( exist => $foo ); # warns "$foo does not exist" if it doesn't
$ass->should_not( exist => $foo ); # warns "$foo exists" if it does.
Note the test itself can only see the arguments passed directly to it at the calling point.
-handlers
Each of the various assertion types have a handler underlying them, which can be overridden during construction.
->new( -handlers => { should => sub { ... } } );
This for instance will override the default handler for "should" and will be invoked somewhere after the result from
$assertion->should( )
Is obtained.
An example handler approximating the default should
handler.
sub {
my ( $status, $message, $name, @slurpy ) = @_;
# $status is the 0/1 returned by the test.
# $message is the message the test gave.
# $name is the name of the test invoked ( ie: ->should( foo => ... )
# @slurpy is the arguments passed from the user to the test.
carp $message if $status;
return $slurpy[0];
}
Its worth noting that handlers dictate in entirety:
What calls will be invoked in response to the fail/pass returned by the test
What will be returned to the caller who invoked the test
For instance, the test
handler is simply:
sub {
my ( $status ) = @_;
return $status;
}
And you could perhaps change that to
sub {
my ( $status, $message ) = @_;
return $message;
}
And then invoking
->test( foo => @args );
Would return foo
's message instead of its return value.
Use this power with care.
Custom Handlers
You can of course define custom handlers outside the core functionality, except of course they won't be accessible as convenient methods.
You can perhaps invoke them via
->_assert( $handler_name, $test_name, @slurpy_args )
But it would be probably nicer for you to sub-class Generic::Assertions
and make it available as a native method:
->$handler_name( $test_name, @slurpy_args )
-input_transformer
You can specify a CodeRef
through which all tests get passed as a primary step.
->new(
-input_transformer => sub {
# Gets both the name, and all the tests arguments
my ( $name, $path ) = @_;
# Returns a substitute argument list
return path( $path );
},
exist => sub {
return ( 0, "$_[0] does not exist" ) unless $_[0]->exists;
return ( 1, "$_[1] exists" );
},
);
...
# The following code will now check that foo.pm exist
# and if it exists, return a path() object for it as $rval.
# If foo.pm does not exist, it will warn.
#
# $rval will be a path object in both cases.
my $rval = $ass->should( exist => "./foo.pm" );
# Under default configuration, this is basically the same as:
sub should_exist {
my @args = @_;
my $path = path($args[0]);
if ( not $path->exists() ) {
warn "$path does not exist";
}
return $path;
}
my $rval = $thing->should_exist("./foo.pm");
# Except of course more composable.
test
Default implementation simply returns the result of the given test.
if ( $assertion->test( test_name => @args ) ) {
}
log
Default implementation 'carp's the message and status given by test_name
, and returns $args[0]
$assertion->log( test_name => @args );
should
Default implementation carps if test_name
returns false
with the message provided by test_name
. It then returns $args[0]
$assertion->should( test_name => @args );
should_not
Default implementation carps if test_name
returns true
with the message provided by test_name
. It then returns $args[0]
$assertion->should_not( test_name => @args );
must
Default implementation croaks if test_name
returns false
with the message provided by test_name
.
$assertion->must( test_name => @args );
must_not
Default implementation croaks if test_name
returns true
with the message provided by test_name
.
$assertion->must_not( test_name => @args );
THANKS
To David Golden/xdg for oversight on some of the design concerns on this module.
It would be for sure much uglier than it presently is without his help :)
AUTHOR
Kent Fredric <kentnl@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.