Generic::Assertions - A Generic Assertion checking class
version 0.001002
This is pre-release code, and as such API is very much subject to change.
API
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.
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 ); }
Generic::Assertions allows you to create portable containers of classes of assertions, and allows keeping severity of assertions from their implementation.
Generic::Assertions
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. )
should
must
[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 )
$instance->severity_level( test_name => @args_for_test )
new
Constructs a Generic::Assertions object.
my $assertion = Generic::Assertions->new( ARGS );
The following forms of ARGS is supported:
ARGS
->new( key => value ); ->new({ key => value });
All keys without a - prefix are assumed to be test names, and are equivalent to:
keys
-
->new( -tests => { key => value } );
-tests
All tests must have a simple string key, and a CodeRef value.
CodeRef
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.
true
false
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:
test
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.
foo
Use this power with care.
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.
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]
test_name
$args[0]
$assertion->log( test_name => @args );
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 );
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 );
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 :)
Kent Fredric <kentnl@cpan.org>
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.
To install Generic::Assertions, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Generic::Assertions
CPAN shell
perl -MCPAN -e shell install Generic::Assertions
For more information on module installation, please visit the detailed CPAN module installation guide.