Perl Moose类的依赖注入
我有一个Moose类,它需要发送类型为Perl Moose类的依赖注入,perl,dependency-injection,moose,Perl,Dependency Injection,Moose,我有一个Moose类,它需要发送类型为Foo::Request的请求。我需要从外部访问此依赖关系,以便在测试中轻松交换请求实现。我提出了以下属性: has request_builder => ( is => 'rw', isa => 'CodeRef', default => sub { sub { Foo::Request->new(@_) } } ); 然后在代码中: my $self = shift; my
Foo::Request
的请求。我需要从外部访问此依赖关系,以便在测试中轻松交换请求实现。我提出了以下属性:
has request_builder => (
is => 'rw',
isa => 'CodeRef',
default => sub {
sub { Foo::Request->new(@_) }
}
);
然后在代码中:
my $self = shift;
my $request = $self->request_builder->(path => …);
在测试中:
my $tested_class = …;
my $request = Test::MockObject->new;
$request->mock(…);
$tested_class->request_builder(sub { $request });
有更简单/更惯用的解决方案吗?考虑以下方法: 在Moose类中定义一个名为
make\u request
的“抽象”方法。然后定义两个实现make_request
的角色——一个调用Foo::request->new
,另一个调用Test::MockObject->new
例如:
您的主类和两个角色:
package MainMooseClass;
use Moose;
...
# Note: this class requires a role that
# provides an implementation of 'make_request'
package MakeRequestWithFoo;
use Moose::Role;
use Foo::Request; # or require it
sub make_request { Foo::Request->new(...) }
package MakeRequestWithMock;
use Moose::Role;
use Test::MockRequest; # or require it
sub make_request { Test::MockRequest->new(...) }
如果要测试主类,请将其与“MakeRequestWithMock”角色混合使用:
package TestVersionOfMainMooseClass;
use Moose;
extends 'MainMooseClass';
with 'MakeRequestWithMock';
package main;
my $test_object = TestVersionOfMainMooseClass->new(...);
如果您想将其用于“make_request”的Foo实现,请将其与“MakeRequestWithFoo”角色混合使用
一些优点:
您将只加载所需的模块。例如,类TestVersionOfMainMooseClass
将不会加载模块Foo::Request
您可以将实现make_request
所需的相关数据添加为新类的实例成员。例如,您最初使用CODEREF的方法可以通过以下角色实现:
package MakeRequestWithCodeRef;
use Moose::Role;
has request_builder => (
is => 'rw',
isa => 'CodeRef',
required => 1,
);
sub make_request { my $self = shift; $self->request_builder->(@_) };
要使用此类,您需要为请求\u生成器
提供初始值设定项,例如:
package Example;
use Moose;
extends 'MainMooseClass';
with 'MakeRequestWithCodeRef';
package main;
my $object = Example->new(request_builder => sub { ... });
最后,您编写的角色可能可用于其他类。在使用Moose::Util::apply_all_角色的测试中动态应用角色如何?我想用这个已经有一段时间了,但还没有借口。以下是我认为它将如何工作 首先,稍微修改原始属性:
package MyClientThing;
has request => (
is => 'rw',
isa => 'Foo::Request',
builder => '_build_request',
);
sub _build_request { Foo::Request->new };
....
然后创建一个Test::RequestBuilder角色:
package Test::RequestBuilder;
use Moose::Role;
use Test::Foo::Request; # this module could inherit from Foo::Request I guess?
sub _build_request { return Test::Foo::Request->new };
同时,在“t/我的客户”一文中,你可以这样写:
use MyClientThing;
use Moose::Util qw( apply_all_roles );
use Test::More;
my $client = MyClientThing->new;
apply_all_roles( $client, 'Test::RequestBuilder' );
isa_ok $client->request, 'Test::Foo::Request';
有关更多信息,请参见。我的建议是,按照Coloric文章中的模型(上面由Mike评论),如下所示: 在你们班:
has request => (
is => 'ro',
isa => 'CodeRef',
default => sub {
Foo::Request->new(@_)
}
);
在您的测试中:
my $request = Test::MockObject->new;
$request->mock(…);
my $tested_class = MyClass->new(request => $request, ...);
与代码完全相同,并进行以下改进:
请求
属性是一个随时可用的对象;无需取消对子参考的引用我知道这篇文章有点老,但是对于现在提到这个问题的人来说,请求者可以使用这样的框架。这并不能回答你的问题,但是你可能会对Coloric的一篇短文感兴趣,它讨论了同样的技术:我喜欢这个解决方案,这就是我将如何解决它。coderef在如何构造对象方面给了你很大的灵活性。@Mike:谢谢你的链接。我以前读过这篇文章,在发布这个问题之前,我重新阅读了它。这是一本很好的读物,但并不是针对建筑商的。这只是针对“一次性”依赖项,而不是您的类需要动态构建的依赖项。实际上,如果您必须在类实现中使用生成器,那么您的示例就是正确的。谢谢。是的,没错,我需要一个构建器,所以我不能使用Coloric博客文章中介绍的简单对象设置器。看起来coderef构建器是最好的解决方案。