# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2020 -- leonerd@leonerd.org.uk
package Test::ExpectAndCheck::Future;
use strict;
use warnings;
use base qw( Test::ExpectAndCheck );
our $VERSION = '0.02';
use constant EXPECTATION_CLASS => "Test::ExpectAndCheck::Future::_Expectation";
=head1 NAME
C<Test::ExpectAndCheck::Future> - C<expect/check>-style unit testing with C<Future>-returning methods
=head1 SYNOPSIS
use Test::More;
use Test::ExpectAndCheck::Future;
my ( $controller, $puppet ) = Test::ExpectAndCheck::Future->create;
{
$controller->expect( act => 123, 45 )
->returns( 678 );
is( $puppet->act( 123, 45 )->get, 678, '$puppet->act yields result' );
$controller->check_and_clear( '->act' );
}
done_testing;
=head1 DESCRIPTION
This package creates objects that assist in writing unit tests with mocked
object instances. Each mocked "puppet" instance will expect to receive a given
list of method calls. Each method call is checked that it received the right
arguments, and will return a L<Future> instance to yield the prescribed
result. At the end of each test, each object is checked to ensure all the
expected methods were called.
It is a variation of L<Test::ExpectAndCheck>, assistance around the results
of invoked methods. Every invoked method will return a L<Future> instance. The
L</returns> or L</throws> method can then set the desired eventual result of
that future instance for each expectation.
These return instances are implemented using L<Test::Future::Deferred>, so
they are not immediately ready. Instead they will only become ready after a
toplevel C<await> expression or call to the C<get> method. This should help
unit tests to run similarly to real-world behaviour, where most futures
returned by real-world interfaces (such as IO systems) would not be
immediately ready. This behaviour can be switched off for individual
expectations by using the L</immediately> method.
=cut
package
Test::ExpectAndCheck::Future::_Expectation;
use base qw( Test::ExpectAndCheck::_Expectation );
use Test::Future::Deferred;
use constant {
RETURNS => 3,
FAILURE => 6,
IMMEDIATE => 7,
};
=head1 EXPECTATIONS
=head2 returns
$exp->returns( @result )
Sets the result that the future returned by this method call will yield.
=cut
=head2 fails
$exp->fails( $message )
$exp->fails( $message, $category, @details )
Sets the failure that the future returned by this method call will yield.
=cut
sub fails
{
my $self = shift;
$self->[FAILURE] = [ @_ ];
return $self;
}
=head2 immediately
$exp->returns( ... )->immediately
$exp->fails( ... )->immediately
Switches this expectation to return an immediate future, rather than a
deferred one.
=cut
sub immediately
{
my $self = shift;
$self->[IMMEDIATE]++;
}
sub _result
{
my $self = shift;
if( $self->[IMMEDIATE] ) {
return Future->fail( @{ $self->[FAILURE] } ) if $self->[FAILURE];
return Future->done( @{ $self->[RETURNS] } );
}
else {
return Test::Future::Deferred->fail_later( @{ $self->[FAILURE] } ) if $self->[FAILURE];
return Test::Future::Deferred->done_later( @{ $self->[RETURNS] } );
}
}
=head1 AUTHOR
Paul Evans <leonerd@leonerd.org.uk>
=cut
0x55AA;