http://qs1969.pair.com?node_id=1189735

Hello dear esteemed monks,

More than once I felt an urge to insert a set of Test::More's checks into my application code, for instance when loading a plug-in or validating a complex piece of data. However, Test::More/Test::Builder is best suited to only run inside test scripts.

So I came up with a module to fill the gap. And I'm going to release it to CPAN soon, unless some huge problem is detected.

The idea is as follows:

What usage I can see this far:

Some details (this is basically a copy-and-paste of the project's README):

IN-APP CHECKS

The following is going to perform some checks and output an explanation (similar to a test script output) if something went wrong.
use strict; use warnings; use Test::Contract; my $c = contract { $_[0]->like( $user_input, qr/.../, "Format as expected" ); $_[0]->isa_ok( $some_object, "Some::Class" ); }; if ($c->get_passing) { # so far, so good - move on! } else { croak "Contract failed: ".$c->get_tap; };

Of course, Test::Contract can be instantiated just fine if one needs more fine-grained control.

EXTENDING THE ARSENAL

As said above, the most basic check in Test::Contract is $contract->refute( $what_went_unexpected, $why_we_care_about_it );. This may be viewed as an inverted ok or assert:

sub refute { my ($condition, $message) = @_; ok (!$condition, $message) or diag $condition; };

So all one needs to build a new check is to create a function that returns false when its arguments are fine, and an explanation of failure when they are not. Think pure function, although it may have side effects, e.g. checking that a file exists.

A Test::Contract::Engine::Build module exists to simplify the task further:

package My::Check; use Exporter qw(import); use Test::Contract::Engine::Build; build_refute my_check => sub { my ($got, $expected) = @_; # ... a big and nasty check here }, args => 2, export => 1; 1;

This would create an exported function called my_check in My::Check, as well as a my_check method in Test::Contract itself. So the following code is going to be correct:

use Test::More tests => 1; use My::Check; my_check $foo, $bar, "foo is fine";

And this one, too:

# inside a running application use Test::Contract; use My::Check(); # don't pollute global namespace my $c = Test::Contract->new; $c->my_check( $foo, $bar, "runtime-generated foo is fine, too" ); if (!$c->get_passing) { # ouch, something went wrong with $foo and $bar };

It is also possible to validate the testing module itself, outputting details on specifically the tests with unexpected results:

use Test::More; use Test::Contract::Unit qw(contract_is); use My::Check; my $c = contract { my_check $proper_foo, $bar; my_check $good_foo, $bar; my_check $broken_foo, $bar; my_check $good_foo, $wrong_bar; }; is_contract $c, "1100", "my_check works as expected"; done_testing;

SOME PHILOSOPHY

Using refutation instead of assertion is similar to the falsifiability concept in modern science.

Or, quoting Leo Tolstoy, "All happy families are alike; each unhappy family is unhappy in its own way".

I would be quite happy if this concept goes outside the Perl community.

CONCLUSION

As stated above, this module is going to be released to CPAN, but maybe I'm missing something very obvious here...

My previous submission on the topic: RFC: Test::Refute - extensible unified assertion & testing tool.

The project link again: https://github.com/dallaylaen/perl-test-contract.

Thank you, and hope you enjoyed the reading!