Assert-Refute
view release on metacpan or search on metacpan
lib/Assert/Refute/Build.pm view on Meta::CPAN
use warnings;
our $VERSION = '0.1701';
=head1 NAME
Assert::Refute::Build - tool for extending Assert::Refute suite
=head1 DESCRIPTION
Although arbitrary checks may be created using just the C<refute> function,
they may be cumbersome to use and especially share.
This module takes care of some boilerplate as well as maintains parity
between functional and object-oriented interfaces of L<Assert::Refute>.
=head1 SYNOPSIS
Extending the test suite goes as follows:
package My::Package;
use Assert::Refute::Build;
use parent qw(Exporter);
build_refute is_everything => sub {
return if $_[0] == 42;
return "$_[0] is not answer to life, universe, and everything";
}, export => 1, args => 1;
1;
This can be later used inside production code to check a condition:
use Assert::Refute qw(:all);
use My::Package;
my $fun_check = contract {
is_everything( shift );
};
my $oo_check = contract {
$_[0]->is_everything( $_[1] );
}, need_object => 1;
# ditto
# apply $fun_check or $oo_check to a variable, get result
my $log = $oo_check->apply(137);
$log->is_passing; # nope
$log->get_tap; # get details
This call will create a prototyped function is_everything(...) in the calling
package, with C<args> positional parameters and an optional human-readable
message. (Think C<ok 1>, C<ok 1 'test passed'>).
=head1 FUNCTIONS
All functions are exportable.
=cut
use Carp;
use Data::Dumper;
use Scalar::Util qw(weaken blessed set_prototype looks_like_number refaddr);
use parent qw(Exporter);
our @EXPORT = qw(build_refute current_contract to_scalar);
# NOTE HACK
# If we're being loaded after Test::More, we're *likely* inside a test script
# This has to be re-done properly
# Cannot instantiate *here* because cyclic dependencies
# so wait until current_contract() is called
our $MORE_DETECTED = Test::Builder->can("new") ? 1 : 0;
=head2 build_refute name => \&CODE, %options
This function
=over
=item * accepts a subroutine reference that returns a false value on success
and a brief description of the discrepancy on failure
(e.g. C<"$got != $expected">);
Note that this function does not need to know anything about the testing
environment it is in, it just cares about its arguments
(think I<pure function>).
=item * builds an exportable wrapper around it that would talk to
the most recent L<Assert::Refute::Report> instance;
=item * adds a method with the same name to L<Assert::Refute::Report>
so that object-oriented and functional interfaces
are as close to each other as possible.
=back
As a side effect, Assert::Refute's internals are added to the caller's
C<@CARP_NOT> array so that carp/croak points to where the built function
is actually used.
B<NOTE> One needs to use Exporter explicitly if either C<export>
or C<export_ok> option is in use. This MAY change in the future.
Options may include:
=over
=item * C<export> => 1 - add function to @EXPORT
(Exporter still has to be used by target module explicitly).
=item * C<export_ok> => 1 - add function to @EXPORT_OK (don't export by default).
=item * C<no_create> => 1 - don't generate a function at all, just add to
L<Assert::Refute>'s methods.
=item * C<manual> => 1 - don't generate any code.
Instead, assume that user has already done that and just add a method
to L<Assert::Refute::Report> and a prototyped exportable wrapper.
This may be useful to create refutations based on subcontract or such.
B<[EXPERIMENTAL]>.
( run in 1.448 second using v1.01-cache-2.11-cpan-39bf76dae61 )