如何在Raku中进行单元测试时模拟类方法



假设我有一个这样的类:

class MyClass {
method data-is-valid {
return self!get-data ~~ m{^From};
}
method !get-data {
return 'From Internet';
}
}

其中CCD_ 1方法从互联网上获取一些数据。

有没有可能模拟这个方法,让它返回我自己的硬编码数据,这样我就可以在不连接互联网的情况下测试模块?

理想情况下,解决方案不应该以任何方式修改类的定义。

注:关于模块的单元测试子程序也存在类似的问题。

我会首先进行重构,将提取逻辑拉到另一个对象,并使MyClass依赖于它:

class Downloader {
method get-data {
return 'From Internet';
}
}
class MyClass {
has Downloader $.downloader .= new;
method data-is-valid {
return $!downloader.get-data ~~ m{^From};
}
}

这是依赖反转的一个例子,这是一种有助于使代码可测试的技术(而且也倾向于使其更容易以其他方式进化(。

通过此更改,现在可以使用Test::Mock模块模拟Downloader:

use Test;
use Test::Mock;
subtest 'Is valid when contains From' => {
my $downloader = mocked Downloader, returning => {
get-data => 'From: blah'
};
my $test = MyClass.new(:$downloader);
ok $test.data-is-valid;
check-mock $downloader,
*.called('get-data', :1times);
}
subtest 'Is not valid when response does not contain From' => {
my $downloader = mocked Downloader, returning => {
get-data => 'To: blah'
};
my $test = MyClass.new(:$downloader);
nok $test.data-is-valid;
check-mock $downloader,
*.called('get-data', :1times);
}

您可能想看看Test::Mock。从其概要来看:

use Test;
use Test::Mock;
plan 2;
class Foo {
method lol() { 'rofl' }
method wtf() { 'oh ffs' }
}
my $x = mocked(Foo);
$x.lol();
$x.lol();
check-mock($x,
*.called('lol', times => 2),
*.never-called('wtf'),
);

Hi@julio我建议你看看例程的wrap函数,这应该能满足你的需要。。。https://docs.raku.org/language/functions#Routines。。。这包括使用soft;防止内联的pragma

最好的想法可能是重构代码(参见Jonathan的回答(

然而,如果你因为某种原因不能,仍然有其他选择:

如果该方法是公共的,您可以简单地创建一个子类并重写该方法。

例如:

use Test;
class MyClass {
method data-is-valid {
return self.get-data ~~ m{^From};
}
method get-data {
return 'From Internet';
}
}
class MyClassTester is MyClass {
method get-data {
return 'Foobar';
}
}
my MyClassTester $class = MyClassTester.new;
nok $class.data-is-valid, 'Mocked class has invalid data';
done-testing;

如果该方法是私有的,则可以使用p6steve答案中所述的wrap。但是,为了修改私有方法,您需要进行内省。

可以这样做:

use Test;
class MyClass {
method data-is-valid {
return self!get-data ~~ m{^From};
}
method !get-data {
return 'From Internet';
}
}
my $class = MyClass.new;
my Method:D $get-data = $class.^find_private_method: 'get-data';
$get-data.wrap: { 'Foobar' };
nok $class.data-is-valid, 'Mocked class has invalid data';
done-testing;

最新更新