在 Raku 中测试私有方法

Jul*_*lio 9 testing methods metaprogramming private raku

有没有办法在 Raku 中测试私有方法?

我知道理想情况下应该定义针对公共方法的测试,但是有没有办法以“错误的方式”做到这一点?:)

我最初想为从我想测试的类继承的测试定义一个子类并在那里进行测试,但似乎没有继承私有方法。

然后我看到了“信任”例程,但我不想在代码的任何类上引用测试类。

是否有类似通过自省更改方法的“私有”属性之类的东西?

调用/测试私有方法的最佳方法是什么?

Jul*_*lio 8

这可以使用自省来完成。

考虑这是您要测试的类:

class SomeClass {
    has Int $!attribute;

    method set-value(Int $value) returns Nil {
        $!attribute = $value;
        return;
    }

    method get-value returns Int {
        return $!attribute;
    }

    # Private method
    method !increase-value-by(Int $extra) returns Nil {
        $!attribute += $extra;
        return;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以创建这样的测试:

use Test;
use SomeClass;

plan 3;

my SomeClass $some-class = SomeClass.new;
my Method:D $increase-value = $some-class.^find_private_method: 'increase-value-by';

$some-class.set-value: 1;
$increase-value($some-class, 4);
is $some-class.get-value, 5, '1+4 = 5';

$increase-value($some-class, 5);
is $some-class.get-value, 10, '5+5 = 10';

my SomeClass $a-new-class = SomeClass.new;
$a-new-class.set-value: 0;
$increase-value($a-new-class, -1);
is $a-new-class.get-value, -1, '0+(-1) = -1; The method can be used on a new class';

done-testing;
Run Code Online (Sandbox Code Playgroud)

您首先创建该类的一个实例并使用它^find_private_method来获取其 private Method。然后你可以Method通过传递一个类的实例作为第一个参数来调用它。

这个答案有更完整的解释:

您如何从它们所属的类型之外访问私有方法或属性?

  • 您也可以将其作为方法来调用。`$some-class.$increase-value(4)` (3认同)

rai*_*iph 5

一杯新鲜的茶以及@Julio 和@JJ 的回答启发了以下内容:

class SomeClass { method !private ($foo) { say $foo } }

use MONKEY-TYPING; augment class SomeClass { trusts GLOBAL }

my SomeClass $some-class = SomeClass.new;

$some-class!SomeClass::private(42); # 42
Run Code Online (Sandbox Code Playgroud)

我的解决方案使用猴子打字来调整课程。猴子打字通常是一件很狡猾的事情(因此是 LOUD 编译指示)。但它似乎是为这样的案例量身定制的。用 a 增加班级,trusts GLOBAL鲍勃是你的叔叔。

Raku 需要SomeClass::资格才能工作。(也许当 RakuAST 宏到来时,会有一个整洁的方法来解决这个问题。)我的倾向是认为必须编写类资格是可以的,并且上面的解决方案比下面的要好得多,但是 YMMV...

也许,相反:

use MONKEY-TYPING;
augment class SomeClass {
  multi method FALLBACK ($name where .starts-with('!!!'), |args) {
    .(self, |args) with $?CLASS.^find_private_method: $name.substr: 3
  }
}
Run Code Online (Sandbox Code Playgroud)

进而:

$some-class.'!!!private'(42); # 42
Run Code Online (Sandbox Code Playgroud)

我用过:

  • Amulti表示FALLBACK,并且要求方法名称字符串以!!!;开头。

  • 常规方法调用(.不是!);

  • 通过名称的字符串版本调用该方法。

multi!!!在情况下,类已经被测试有一个或多个FALLBACK声明的方法。

前置约定!!!似乎或多或少可以保证测试代码永远不会干扰类的工作方式。(特别是,如果有一些对不存在的私有方法的调用,并且存在FALLBACK处理,它会处理这种情况,而FALLBACK不会涉及到这只猴子。)

它还应该提醒任何阅读测试代码的人正在发生一些奇怪的事情,在极不可能的情况下确实开始发生一些奇怪的事情,要么是因为我错过了一些我看不到的东西,要么是因为FALLBACK类中的某些代码碰巧使用相同的约定。

  • 很好的答案!我认为您提出的第一种方法比第二种方法干净得多。即使使用 MONKEY 编译指示,我仍然认为它是比使用内省*对于这种特殊情况*(测试)更好的解决方案。另外,我关于内省的回答已经在另一个问题中以更详细的方式涵盖了。所以我接受你的答案,它以不同的方式涵盖了这个问题,让我学到了很酷的东西。谢谢! (2认同)