【问题标题】:Mock a Perl module having multiple packages模拟具有多个包的 Perl 模块
【发布时间】:2014-10-28 15:53:50
【问题描述】:

我有以下场景,我需要模拟具有多个包的模块中的子例程。

我的模块(moddemo.pm)

package A;

sub helloa
{
  print "Hello, Im in demomod module of package A\n";
  return 1;
}

package B;

sub hellob
{
 print "Hello, Im in demomod module of package B\n";
 return 1;
}

1;

我需要模拟 A::helloa()、B::hellob() 子例程。这是我尝试过的代码,但失败了。

#!/usr/bin/perl
use Test::MockModule;
use moddemo;

{
 my $mockobj = new Test::MockModule('moddemo');
 $mockobj->mock('A::helloa', sub { print "This is mocked object calling helloa function\n"; });
 $mockobj->mock('B::hellob', sub { print "This is mocked object calling hellob function\n";});
 modobj::A::helloa();
 modobj::B::hellob();
}

任何帮助如何模拟这些子例程?提前致谢。

【问题讨论】:

    标签: perl unit-testing mocking package perl-module


    【解决方案1】:

    我已经通过重新定义子程序来做到这一点。

    use strict;
    use warnings;
    use Demo;
    
    A::helloa();
    {
        no warnings 'redefine';
        local *A::helloa = sub {
            print "helloa has changed\n";
            return 1;
        };
        A::helloa();
    }
    A::helloa();
    

    注意事项:

    • 重新定义会导致警告,所以需要no warnings 行来消除它。
    • 重新定义是在本地应用的,因此您应该看到只有块内的子例程调用发生了变化。

    【讨论】:

    • 非常感谢。这很好!但是当模块有多个包时,是否建议使用这种方法而不是模拟?
    • 我这样做是因为使用 Test::MockModule 无法使用与 .pm 文件命名不同的包。我想你可以认为它是一种更手动的方式。在这种情况下,另一个更适合您的模块是metacpan.org/pod/Sub::Override
    【解决方案2】:

    我可能是错的,但从我在module source 中看到的情况来看,似乎不可能以您尝试的方式使用它。 MockModule 首先requires 需要的包(.pm 文件)然后只允许来自该包的子例程,即不允许名称中的::

    据我了解,它可以以这种方式用于测试模块(修改您的代码以符合其期望):

    moddemo.pm:

    package moddemo;
    use strict;
    use warnings;
    
    sub helloa
    {
            print "moddemo::helloa\n";
            return 1;
    }
    
    sub hellob
    {
            print "moddemo::hellob\n";
            return 1;
    }
    
    1;
    

    现在是测试文件,test.pl:

    use strict;
    use warnings;
    
    use Test::MockModule;
    use moddemo;
    
    {
            my $mockobj = new Test::MockModule('moddemo');
            $mockobj->mock('helloa', sub { print "This is mocked object calling helloa function\n"; });
            $mockobj->mock('hellob', sub { print "This is mocked object calling hellob function\n";});
    
            # mocked versions
            moddemo::helloa();
            moddemo::hellob();
    }
    
    # now original versions
    moddemo::helloa();
    moddemo::hellob();
    

    最后是预期的输出:

    This is mocked object calling helloa function
    This is mocked object calling hellob function
    moddemo::helloa
    moddemo::hellob
    

    【讨论】:

    • 感谢您的回答。是的,我以前试过这个,效果很好。但问题是当模块有多个包时。除了 Test::MockModule 之外,还有其他 Perl 模块符合我的期望吗?
    • 不知道任何 CPAN 模块(当然它可能存在,但我不能说出一个),但你至少可以看看 Test::MockModule 实现(它保存原始子例程的地方sub mock 中的实现,以及替换 sub _replace_sub 中代码的代码)并使用相同的 typeglob 技术手动创建模拟对象。 Typeglobs (*name) 在perlmod(1) 中有描述。
    猜你喜欢
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多