如何完整Mock Perl的Net::Twitter模块?
如何完整Mock Net::Twitter模块?
我在测试中想要Mock Net::Twitter模块,但发现似乎只能Mock构造函数或方法中的一个。
被测代码
package mcve; use strict; use warnings; use Net::Twitter; sub mcve() { my $twitter = Net::Twitter->new( 'traits' => [qw/API::RESTv1_1/], 'ssl' => 1, ); # 假设后续会调用 $twitter->update(...) } 1;
初始测试用例(仅Mock构造函数new时测试通过)
use strict; use warnings; use base 'Test::Class'; use Test::More; use Test::MockModule; use mcve; __PACKAGE__->runtests() unless caller; sub mcve : Tests { my %twitter_args; my $twitter = Test::MockModule->new('Net::Twitter'); $twitter->redefine('new', sub { shift @_; %twitter_args = @_; return; }); mcve::mcve(); is_deeply( \%twitter_args, { 'traits' => ['API::RESTv1_1'], 'ssl' => 1, } ); }
同时Mockupdate方法时的报错
robert@saaz:~/mcve$ prove -I. mcve.t mcve.t .. 1/? # Failed test 'mcve died (Net::Twitter::update does not exist! at mcve.t line 22.)' # at mcve.t line 8. # (in main->mcve) # Looks like you failed 1 test of 1. mcve.t .. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/1 subtests Test Summary Report ------------------- mcve.t (Wstat: 256 Tests: 1 Failed: 1) Failed test: 1 Non-zero exit status: 1 Files=1, Tests=1, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.33 cusr 0.03 csys = 0.37 CPU) Result: FAIL
尝试过的无效方法
- 调整Mock目标为
ref(Net::Twitter->new(traits => [qw/API::RESTv1_1/])):不再报错,但构造函数的Mock未被调用,测试失败。 - 使用
Test::MockModule->new('Net::Twitter', no_auto => 1);:无效果。
解决方案
问题核心在于update方法并非Net::Twitter基类的方法,而是来自你指定的API::RESTv1_1 trait(实际是Moose Role),直接MockNet::Twitter找不到该方法。以下两种方案可解决:
方案1:让Mock构造函数返回带自定义方法的对象
在Mocknew时,返回一个包含你需要Mock的方法的对象,这样既可以验证构造参数,又能Mock目标方法:
use strict; use warnings; use base 'Test::Class'; use Test::More; use Test::MockModule; use mcve; __PACKAGE__->runtests() unless caller; sub mcve : Tests { my %twitter_args; my $update_call_count = 0; my $last_update_text; my $twitter_mock = Test::MockModule->new('Net::Twitter'); $twitter_mock->redefine('new', sub { shift @_; %twitter_args = @_; # 返回一个包含Mock update方法的bless对象 return bless { update => sub { $update_call_count++; $last_update_text = shift; return { id => 12345 }; } }, 'Net::Twitter::Mock'; }); mcve::mcve(); # 验证构造参数 is_deeply( \%twitter_args, { 'traits' => ['API::RESTv1_1'], 'ssl' => 1 }, '构造函数接收参数正确' ); # 假设被测代码调用了update,这里验证调用情况 # 比如在mcve子中添加 $twitter->update('Hello World'); # ok($update_call_count == 1, 'update方法被调用一次'); # is($last_update_text, 'Hello World', 'update方法接收参数正确'); }
方案2:Mock对应的Trait/Role模块
先加载API::RESTv1_1对应的Role模块,然后直接Mock该模块的update方法。这种方式适合你需要保留部分真实构造逻辑的场景:
use strict; use warnings; use base 'Test::Class'; use Test::More; use Test::MockModule; use mcve; # 先加载对应的Role模块 use Net::Twitter::Role::API::RESTv1_1; __PACKAGE__->runtests() unless caller; sub mcve : Tests { my %twitter_args; my $update_call_count = 0; # Mock构造函数验证参数 my $twitter_mock = Test::MockModule->new('Net::Twitter'); $twitter_mock->redefine('new', sub { shift @_; %twitter_args = @_; # 返回真实的Net::Twitter实例(会自动应用traits) return $twitter_mock->original('new')->(@_); }); # Mock Role中的update方法 my $rest_mock = Test::MockModule->new('Net::Twitter::Role::API::RESTv1_1'); $rest_mock->redefine('update', sub { $update_call_count++; return { id => 67890 }; }); mcve::mcve(); is_deeply( \%twitter_args, { 'traits' => ['API::RESTv1_1'], 'ssl' => 1 }, '构造参数正确' ); # 验证update调用 # ok($update_call_count == 1, 'update方法被调用'); }
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

