如何在对模块进行单元测试时模拟导入的子例程

时间:2017-03-07 10:28:59

标签: unit-testing mocking perl6

考虑一个模块,它导出一个连接到Internet并返回结果的子例程:

unit module A;

sub download is export {
     "result from internet"  # Not the actual implementation, obviously.
}

另一个导入并调用该子程序的模块:

use A;  # imports &download into this lexical scope
unit module B;

sub do-something is export {
     download().uc ~ "!!"   # Does something which involves calling &download
}

现在我想为模块B编写单元测试 但我不希望测试真正连接到互联网;我希望他们使用由我的测试脚本控制的子程序download的模拟版本:

use Test;
plan 2;

use B;

my $mock-result;
my &mock-download = -> { $mock-result }

# ...Here goes magic code that installs &mock-download
# as &download in B's lexical scope...

$mock-result = "fake result";
is do-something(), "FAKE RESULT!!", "do-something works - 1";

$mock-result = "foobar";
is do-something(), "FOOBAR!!", "do-something works - 2";

问题是缺少覆盖子download ...

的魔术代码

在Perl 5中,我认为使用glob赋值可以很容易地实现这一点,或者在Sub::OverrideTest::MockModule的帮助下更好。

但在Perl 6中,模块B的词法范围在完成编译后关闭,因此在测试脚本运行时不再被修改(如果我错了,请纠正我) )。所以这种方法似乎不可能。

如何在Perl 6中解决这个任务呢? 即如何为B::do-something编写单元测试,而不让它调用真实的A::download

1 个答案:

答案 0 :(得分:6)

最简单的方法可能是使用https://docs.perl6.org/language/functions#Routines中描述的wrap,但前提条件是阻止内联的use soft;编译指示。您需要在模块A中use soft;

unit module A;
use soft;

sub download is export {
    "result from internet";
}

模块B:

unit module B;
use A;

sub do-something is export {
    download.uc ~ "!!";
}

测试脚本:

use Test;
use A;
use B;

&download.wrap({
    "mock result";
});

is do-something, "MOCK RESULT!!", "mock a 'use'd sub";
# ok 1 - mock a 'use'd sub