You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何完整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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:35:24