単体テストなどで「あるオブジェクトの特定のメソッドが呼ばれたかどうか」を検証したい時はスパイ(Spy)を使うといい。Test::MockObject::Extendsを使うと、以下のようにしてスパイを実現できる。
spy.t
package Sample; ######## Test target object
use strict;
use warnings;
sub new {
my ($class) = @_;
return bless {}, $class;
}
sub foo {
return "bar";
}
package main; ######## Test code
use strict;
use warnings;
use Test::More;
use Test::MockObject::Extends;
my $sample = Sample->new;
my @foo_log = ();
Test::MockObject::Extends->new($sample);
my $original = $sample->can("foo");
$sample->mock(foo => sub {
push(@foo_log, [@_]);
goto $original;
});
is($sample->foo, "bar", "1st foo");
is($sample->foo(2), "bar", "2nd foo");
is($sample->foo(3, "three"), "bar", "3rd foo");
is_deeply(\@foo_log, [ [$sample], [$sample, 2], [$sample, 3, "three"] ], "log OK");
done_testing;
ポイントは、
my $original = $sample->can("foo");
を、モックメソッドの定義前に呼び出してオリジナルメソッドのcoderefを取得しておく点。モックメソッド内でgoto $original
とすれば何事もなかったかのようにオリジナルメソッドを実行できる。
別の方法?
- Test::MockModuleでもほぼ同じ方法が使える。こちらはTest::MockModuleオブジェクトからオリジナルメソッドを取得できる。
- もうちょっと高級な方法が好みならTest::Mock::RecorderやTest::Doubleがよさそう。
- メソッドではなく単純なcoderefのスパイならSub::Spyというモジュールが使えそう。