Skip to content

Commit

Permalink
Provide define method so the mock method is unneeded in tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
toddr authored and geofffranks committed Oct 19, 2019
1 parent 2ef289a commit 0298d8d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
34 changes: 32 additions & 2 deletions lib/Test/MockModule.pm
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ sub redefine {
return $self->mock(@_);
}

sub define {
my ($self, @mocks) = (shift, @_);

while ( my ($name, $value) = splice @mocks, 0, 2 ) {
my $sub_name = $self->_full_name($name);
my $coderef = *{$sub_name}{'CODE'};

if ('CODE' eq ref $coderef) {
croak "$sub_name exists!";
}
}

return $self->mock(@_);
}

sub mock {
my $self = shift;

Expand Down Expand Up @@ -208,9 +223,12 @@ Test::MockModule - Override subroutines in a module for unit testing
$module->mock('subroutine', sub { ... });
Module::Name::subroutine(@args); # mocked
#Same effect, but this will die() if other_subroutine()
#doesn't already exist, which is often desirable.
# Same effect, but this will die() if other_subroutine()
# doesn't already exist, which is often desirable.
$module->redefine('other_subroutine', sub { ... });
# This will die() if another_subroutine() is defined.
$module->define('another_subroutine', sub { ... });
}
Module::Name::subroutine(@args); # original subroutine
Expand Down Expand Up @@ -356,6 +374,18 @@ code path that no longer behaves consistently with the mocked behavior.
Note that redefine is also now checking if one of the parent provides the sub
and will not die if it's available in the chain.
=item define($subroutine)
The reverse of redefine, this will fail if the passed subroutine exists.
While this use case is rare, there are times where the perl code you are
testing is inspecting a package and adding a missing subroutine is actually
what you want to do.
By using define, you're asserting that the subroutine you want to be mocked
should not exist in advance.
Note: define does not check for inheritance like redefine.
=item original($subroutine)
Returns the original (unmocked) subroutine
Expand Down
35 changes: 35 additions & 0 deletions t/define.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use warnings;
use strict;

use Test::More;
use Test::Warnings;

use Test::MockModule;

my $mocker = Test::MockModule->new('Mockee');

$mocker->define( 'doesnt_exist', 2 );
is( Mockee::doesnt_exist(), 2, 'define() allows us to mock nonexistant subroutines.' );

eval { $mocker->define( 'existing_subroutine', 6 ) };
like( $@, qr/Mockee::existing_subroutine exists\!/, 'exception when define()ing an existing subroutine' );

undef $mocker;
is( Mockee->can('doesnt_exist'), undef, "the defined sub went away after mocker is undeffed" );
$mocker = Test::MockModule->new('Mockee');

$mocker->define( 'doesnt_exist', 3 );
is( Mockee::doesnt_exist(), 3, 'The subroutine can be defined again after the mock object goes out of scope and is re-instantiated.' );

done_testing();

#----------------------------------------------------------------------

package Mockee;

our $VERSION;
BEGIN { $VERSION = 1 }

sub existing_subroutine { 1 }

1;

0 comments on commit 0298d8d

Please sign in to comment.