如何模拟Moo角色中定义的方法?

时间:2016-06-23 17:54:25

标签: perl unit-testing moose moo

鉴于以下角色:

package MyRole;
use Moo::Role;

sub foo { 
    return 'blah';
}

以下消费类:

package MyClass;
use Moo;
with 'MyRole';

around foo = sub { 
    my ($orig, $self) = @_;
    return 'bak' if $self->$orig eq 'baz';
    return $self->$orig;
}

我想测试around修饰符中定义的行为。我该怎么做呢? Test :: MockModule似乎无法工作:

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';

编辑:我想要测试的是MyClass与MyRole的交互,如around修饰符中所定义。我想测试around修饰符中的代码是否符合我的想法。这是另一个与我的实际代码更接近的例子:

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);
};

所以我想在这里做的是模拟MyRole2::call以返回一个静态文档,该文档在我的测试装置中定义,包含错误并测试异常是否被正确抛出。我知道如何使用Test::More::throws_ok或类似方法对其进行测试。我不知道该怎么做是模拟 MyRole2 :: call MyClass2::call

2 个答案:

答案 0 :(得分:1)

来自#moose上的mst:

datetime

诀窍是在之前覆盖MyRole :: foo 任何使用它的东西都会被加载。这意味着使用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'; } 代替require MyClass,因为use MyClass会转换为use MyClass,它会在之前覆盖之前的所有内容,而加载。

答案 1 :(得分:0)

可以使用Test::MockModule

完成

这些是所需的微小变化:

  1. around foo {应写为around foo => sub {,因为around需要子程序参考。

  2. $self->$orig需要写为$self->($orig)

  3. 文档将其列为my ($orig, $self) = @_;,因此我将其更改为$orig->($self);

  4. 这是一个工作版本:

    <强> MyRole.pm

    package MyRole;
    use Moo::Role;
    
    sub foo { 
        return 'foo blah';
    }
    
    sub bar { 
        return 'bar blah';
    }
    
    1;
    

    <强> MyClass.pm

    package MyClass;
    
    use Moo;
    with 'MyRole';
    
    around foo => sub { 
        my ($orig, $self) = (@_);
        my ($result) = $orig->($self);
        return 'bak' if $result eq 'baz'; # Will never return 'bak' as coded.
        return $result;
    };
    

    <强> test.t

    #!/usr/bin/env perl
    
    use MyClass;
    use Test::Most;
    use Test::MockModule;
    
    my $obj = MyClass->new;
    # foo has an around block, bar does not
    is($obj->bar, 'bar blah', 'bar() returns [ bar blah ]');
    is($obj->foo, 'foo blah', 'foo() returns [ foo blah ]');
    
    my $mock = Test::MockModule->new('MyClass');
    $mock->mock('foo' => sub { return 'mocked foo blah' } );
    
    my $mocked = MyClass->new;
    is($mocked->bar, 'bar blah', 'bar() still returns [ bar blah ]');
    is($mocked->foo, 'mocked foo blah', 'foo() now returns mocked answer [ mocked foo blah ]');
    

    运行

    prove -v test.t
    test.t .. 
    ok 1 - bar() returns [ bar blah ]
    ok 2 - foo() returns [ foo blah ]
    ok 3 - bar() still returns [ bar blah ]
    ok 4 - foo() now returns mocked answer [ mocked foo blah ]
    1..4
    ok
    All tests successful.
    Files=1, Tests=4,  0 wallclock secs ( 0.06 usr  0.01 sys +  0.19 cusr  0.00 csys =  0.26 CPU)
    Result: PASS
    

    请看一下:

    Class::Method::Modifiers::around()