我正在尝试对使用来自CPAN的Importer::Git
的模块Git.pm
进行单元测试,我想模拟对Git::command
,Git::repository
和{{1 }}等不会真正更改我的文件系统。我试图通过Test :: MockObject做到这一点,但似乎我还没有完全了解其内部工作原理...
示例:
Git::command_oneline
测试用例:
package Importer::Git
sub create_repository {
my ( $rc, $repo );
$rc = Git::command_oneline( 'init', $self->targetdir . "/" . $self->name );
$repo = Git->repository( Directory => $self->targetdir . "/" . $self->name );
$rc = $repo->command( 'config', 'user.name', $self->git_import_user->{ name } );
$self->_repo( $repo );
return $repo;
}
但是它似乎并没有取代有问题的git对象,因为该测试崩溃时来自实际Git.pm模块的消息。
use Import::Git;
use Test::More tests => 1; # last test to print
use Test::Exception;
use Test::MockObject;
# pretend to have loaded the Git Module.
$INC{'Git.pm'} = 1;
my $git = Test::MockObject->new();
$git->fake_module('Git', repository => sub { $git } );
$git->set_true( qw(command command_oneline) );
$repo = Import::Git->init();
$repo->targetdir('./');
$repo->name('testrepo');
$repo->git_import_user({ name => 'test', email => 'test@test.com', push_default => 'testpush' });
$repo->create_repository();
我猜在Importer :: Git的这两行中,它不能代替$ repo
error: Malformed value for push.default: testpush
error: Must be one of nothing, matching, simple, upstream or current.
fatal: bad config variable 'push.default' in file '/home/.../testrepo/.git/config' at line 10
init .//testrepo: command returned error: 128
那么我该如何正确模拟呢?我希望 $repo = Git->repository( Directory => $self->targetdir . "/" . $self->name );
$rc = $repo->command( 'config', 'user.name', $self->git_import_user->{ name } );
调用仅返回1。
更新:
戴夫斯猜想是对的。更正代码以解决此问题:
$repo->command
答案 0 :(得分:3)
这只是一个猜测,我没有时间进行测试...
The documentation对于fake_module()
方法来说是这样的:
请注意,这必须在实际模块有机会加载之前进行。在使用前将其包装在BEGIN块中,或者在
use_ok()
或require_ok()
调用之前将其包装。
在加载模块之前,我希望您的模块(是Import::Git
还是Importer::Git?) loads
Git.pm , so you need to call
fake_module()`。类似这样的东西,也许:
use Test::More tests => 1; # last test to print
use Test::Exception;
use Test::MockObject;
my $git;
BEGIN {
# pretend to have loaded the Git Module.
$INC{'Git.pm'} = 1;
$git = Test::MockObject->new();
$git->fake_module('Git', repository => sub { $git } );
$git->set_true( qw(command command_oneline) );
}
use Import::Git;
答案 1 :(得分:1)
Dave's excellent answer的替代方法是完全不使用fake_module
功能,而使用Sub::Override之类的方法临时覆盖构造函数。我觉得这可以提供更精细的控制。
use Test::More;
use Test::MockObject;
use Sub::Override;
use Git (); # we need this so we can override it in case it hasn't been loaded yet
# test code ...
{
# this is our faked module
my $git = Test::MockObject->new;
$git->set_true( qw(command command_oneline) );
# we will save the arguments to the constructor here for
# inspection in tests later
my @constructor_args;
# this will temporarily replace the constructor until $sub
# goes out of scope
my $sub = Sub::Override->new(
'Git::new' => sub {
@constructor_args = @_; # save args
return $git; # return fake
}
);
is something_that_deals_with_git(), $what_you_expect, 'test stuff with git';
is scalar @constructor_args, 2, '... and Git constructor was called with 2 args';
# ...
}
它的缺点是您不能使用Test::MockObject's facilities to look at the calls到new
,但是有一个简单的方法可以通过我们的@constructor_args
变量来减轻这种情况。其他所有内容都保持不变。