package Test::MockSub;
sub mock {
my $self = bless {}, shift;
my $sub = shift;
%{ $self } = @_;
if (defined $self->{return_value} && defined $self->{side_effect}){
die "use only one of return_value or side_effect";
}
my $called;
{
no strict 'refs';
*$sub = sub {
$self->{call_count} = ++$called;
return $self->{return_value} if defined $self->{return_value};
$self->{side_effect}->() if $self->{side_effect};
};
}
return $self;
}
sub called {
return shift->call_count ? 1 : 0;
}
sub call_count {
return shift->{call_count};
}
sub reset {
my $self = shift;
delete $self->{$_} for keys %{ $self };
}
1;
####
package MyPackage;
use lib '.';
use One;
sub test {
my $obj = One->new;
$obj->foo;
}
1;
####
package One;
sub new {
return bless {}, shift;
}
sub foo {
print "in One::foo\n";
}
1;
####
use warnings;
use strict;
use feature 'say';
use lib '.';
use Test::MockSub;
use MyPackage;
{# called() && call_counnt()
my $foo = Test::MockSub->mock('One::foo');
MyPackage::test;
MyPackage::test;
my $count = $foo->call_count;
say "testing call_count(): $count";
my $called = $foo->called;
say "testing called(): $called";
}
{# return_value
my $foo = Test::MockSub->mock('One::foo', return_value => 'True');
my $ret = MyPackage::test;
say "testing return_value: $ret";
}
{# side_effect
my $cref = sub {die "thowing error";};
my $foo = Test::MockSub->mock('One::foo', side_effect => $cref);
eval{MyPackage::test;};
print "testing side_effect: ";
say $@ ? 'success' : 'failed';
}
{# side_effect && return_value dies()
my $foo;
my $cref = sub {};
eval{
$foo = Test::MockSub->mock('One::foo', side_effect => $cref, return_value => 1);};
print "testing side_effect & return_value dies(): ";
say $@ ? 'success' : 'failed';
}
{# reset()
my $foo = Test::MockSub->mock('One::foo', return_value => 1);
my $ret1 = MyPackage::test;
$foo->reset;
my $ret2 = MyPackage::test;
print "testing reset():";
say defined $ret1 && ! defined $ret2 ? 'success' : 'failed';
}