Given the following Role:
package MyRole;
use Moo::Role;
sub foo {
return 'blah';
}
And the following consuming class:
package MyClass;
use Moo;
with 'MyRole';
around foo = sub {
my ($orig, $self) = @_;
return 'bak' if $self->$orig eq 'baz';
return $self->$orig;
}
I would like to test behaviour defined in the around
modifier. How do I do this? It seems that Test::MockModule won't work:
use MyClass;
use Test::Most;
use Test::MockModule;
my $mock = Test::MockModule->new('MyRole');
$mock->mock('foo' => sub { return 'baz' });
my $obj = MyClass->new;
# Does not work
is $obj->foo, 'bak', 'Foo is what it oughtta be';
EDIT: What I'm looking to test is the interaction of MyClass with MyRole as defined in the around
modifier. I want to test that the code in the around
modifier does what I think it should. Here's another example that's closer to my actual code:
package MyRole2
use Moo::Role;
sub call {
my $self = shift;
# Connect to server, retrieve a document
my $document = $self->get_document;
return $document;
}
package MyClass2;
use Moo;
with 'MyRole2';
around call = sub {
my ($orig, $self) = @_;
my $document = $self->$orig;
if (has_error($document)) {
die 'Error';
}
return parse($document);
};
So what I want to do here is to mock MyRole2::call
to return a static document, defined in my test fixtures, that contains errors and test that the exception is thrown properly. I know how to test it using Test::More::throws_ok
or similar. What I don't know how to do is to mock MyRole2::call and not MyClass2::call
.
From mst on #moose:
use 5.016;
use Test::Most tests => 1;
require MyRole;
our $orig = MyRole->can('foo');
no warnings 'redefine';
*MyRole::foo = sub { goto &$orig };
{
local $orig = sub {'baz'};
require MyClass;
my $obj = MyClass->new;
is $obj->foo, 'bak', 'Foo is what it oughtta be';
}
The trick is to override MyRole::foo before anything that uses it gets loaded. Which means using require MyClass
instead of use MyClass
, because use MyClass
translates to BEGIN { require MyClass }
which defeats the whole thing of overriding the method before anything using it gets loaded.