在 Raku 中测试私有方法

Testing private methods in Raku

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

我知道应该理想地定义他们针对 public 方法的测试,但是有没有一种方法可以“以错误的方式”做到这一点? :)

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

然后我看到了 'trusts' routine,但我不想在任何 class 代码中引用测试 class。

是否有类似通过内省更改方法的 'private' 属性 的东西?

call/test 私有方法的最佳方法是什么?

这可以通过内省来完成。

考虑这是您要测试的class:

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;
    }
}

您可以创建这样的测试:

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;

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

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

除了使用内省之外,您还可以尝试使用外部助手角色来访问所有私有方法并直接调用它们。例如:

role Privateer {
    method test-private-method ( $method-name, |c  ) {
    self!"$method-name"(|c);
    }
}

class Privateed does Privateer {
    method !private() { return "⌣"  }
}

my $obj = Privateed.new;
say $obj.test-private-method( "private" );

这里的关键是通过名称调用方法,您可以使用 public 和私有方法,尽管对于私有方法您需要使用它们的特殊语法 self!.

一杯新鲜的茶和@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

我的解决方案使用猴子打字来调整 class。猴子打字通常是一件狡猾的事情(因此是 LOUD pragma)。但它似乎是为这种情况量身定做的。用 trusts GLOBAL 扩充 class 和 Bob 是你的叔叔。

Raku 需要 SomeClass:: 资格才能工作。 (也许当 RakuAST 宏出现时,会有一个巧妙的方法来解决这个问题。)我倾向于认为必须写一个 class 限定是可以的,上面的解决方案比下面的要好得多,但是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
  }
}

然后:

$some-class.'!!!private'(42); # 42

我用过:

  • 一个multiFALLBACK,并且要求方法名字符串以!!!;

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

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

multi!!! 是为了防止被测试的 class 已经声明了一个或多个 FALLBACK 方法。

!!! 前面的约定似乎或多或少保证了测试代码永远不会干扰class 的工作方式。 (特别是,如果有一些对不存在的私有方法的调用,并且存在现有的 FALLBACK 处理,它会处理这种情况而无需这只猴子 FALLBACK 参与。)

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