此模块提供了一个测试框架,并在测试规范的官方套件中使用。它的所有函数都发出符合Test Anything Protocol的输出。

另请参阅在 Raku 中编写和运行测试

方法§

子程序 plan§

multi plan(Cool:D :skip-all($reason)!)
multi plan($number_of_tests)

指定测试数量——通常写在测试文件的开头。

plan 15;   # expect to run 15 tests 

subtest中,plan用于指定子测试中的测试数量。

如果使用了plan,则不必使用done-testing指定测试结束。

您还可以提供一个:skip-all命名参数来代替测试计数,以指示您要跳过所有测试。这样的计划将调用exit,除非在subtest内部使用。

plan :skip-all<These tests are only for Windows> unless $*DISTRO.is-win;
plan 1;
ok dir 'C:/'# this won't get run on non-Windows 

如果在subtest中使用,它将从该subtestCallablereturn。因此,要在subtest内部使用:skip-all,您必须使用sub而不是常规块

plan 2;
subtest "Some Windows tests" => sub { # <-- note the `sub`; can't use bare block 
    plan :skip-all<We aren't on Windows> unless $*DISTRO.is-win;
    plan 1;
    ok dir 'C:/'# this won't get run on non-Windows 
}
ok 42# this will run everywhere and isn't affected by skip-all inside subtest 

请注意,带有:skip-allplan是为了避免执行任何测试而不将测试运行标记为失败(即计划不运行任何内容,这很好)。使用skip-rest跳过所有进一步的测试,一旦运行开始(即计划运行一些测试,甚至可能运行了一些测试,但现在我们正在跳过所有剩下的测试)。使用bail-out使测试运行失败,而不运行任何进一步的测试(即情况非常糟糕,没有必要运行任何其他内容;我们失败了)。

子程序 done-testing§

sub done-testing()

指定测试已完成。当您没有包含要运行的测试数量的plan时,请使用此函数。使用done-testing时不需要plan

建议在所有测试最终确定后,删除done-testing函数并用plan函数替换它。使用plan可以帮助检测出由于测试中的错误或编译器中的错误而意外跳过的测试,否则不会报告测试失败。例如

sub do-stuff {@};
use Test;
ok .is-prime for do-stuff;
done-testing;
# output: 
1..0

上面的示例是done-testing失败的地方。do-stuff()没有返回任何内容,也没有测试任何内容,即使它应该返回结果进行测试。但测试套件不知道应该运行多少个测试,所以它通过了。

添加 plan 可以真实反映测试

sub do-stuff {@};
use Test;
plan 1;
ok .is-prime for do-stuff;
# output: 
1..1
# Looks like you planned 1 test, but ran 0 

请注意,保留 done-testing 不影响新测试结果,但应将其删除以提高清晰度。

如果任何测试失败或运行的测试少于计划,则 done-testing 函数返回 False,否则返回 True

子 ok§

multi ok(Mu $cond$desc = '')

如果给定的 $cond 评估为 True,则 ok 函数将测试标记为通过。它还接受可选的测试描述作为第二个参数。

my $responsemy $query...;
ok $response.success'HTTP response was successful';

原则上,您可以将 ok 用于每种比较测试,方法是将比较包含在传递给 $cond 的表达式中

sub factorial($x{ ... };
ok factorial(6== 720'Factorial - small integer';

但是,如果可能的话,最好使用下面专门的比较测试函数之一,因为如果比较失败,它们可以打印更有用的诊断输出。

子 nok§

multi nok(Mu $cond$desc = '')

如果给定的 $cond 评估为 False,则 nok 函数将测试标记为通过。它还接受可选的测试描述作为第二个参数。

my $responsemy $query...;
nok $query.error'Query completed without error';

子 is§

multi is(Mu $gotMu:U $expected$desc = '')
multi is(Mu $gotMu:D $expected$desc = '')

如果 $got$expected 使用 eq 运算符 进行比较,则将测试标记为通过,除非 $expected 是类型对象,在这种情况下,将使用 === 运算符;接受可选的测试描述作为最后一个参数。

注意:eq 运算符对操作数进行字符串化,这意味着 is() 不是用于测试更复杂内容(例如列表)的良好函数:is (1, (2, (3,))), [1, 2, 3] 通过测试,即使操作数有很大不同。对于这些情况,请使用 is-deeply 例程

my $pdf-documentsub factorial($x{ ... }...;
is $pdf-document.author"Joe"'Retrieving the author field';
is factorial(6),         720,   'Factorial - small integer';
my Int $a;
is $aInt'The variable $a is an unassigned Int';

注意:如果值之间空格不同,则 is() 将以不同的方式输出失败消息,以显示每个值中的空格。例如,在下面的输出中,第二个测试在 got: 行中显示了文字 \t

is "foo\tbar""foo\tbaz";   # expected: 'foo     baz'␤#      got: 'foo   bar' 
is "foo\tbar""foo    bar"# expected: "foo    bar"␤#      got: "foo\tbar" 

子 isnt§

multi isnt(Mu $gotMu:U $expected$desc = '')
multi isnt(Mu $gotMu:D $expected$desc = '')

如果 $got$expected 使用与 is() 相同的规则相等,则将测试标记为通过。该函数接受可选的测试描述。

isnt pi3'The constant π is not equal to 3';
my Int $a = 23;
$a = Nil;
isnt $aNil'Nil should not survive being put in a container';

子 is_approx§

multi is_approx(Mu $gotMu $expected$desc = '')

注意:已在 Rakudo 2023.09 版本中移除,在较早版本中已弃用。请改用 is-approx

子 is-approx§

multi is-approx(Numeric $gotNumeric $expected$desc = '')
multi is-approx(Numeric $gotNumeric $expectedNumeric $abs-tol,
                    $desc = '')
multi is-approx(Numeric $gotNumeric $expected$desc = '',
                    Numeric :$rel-tol is required)
multi is-approx(Numeric $gotNumeric $expected$desc = '',
                    Numeric :$abs-tol is required)
multi is-approx(Numeric $gotNumeric $expected$desc = '',
                    Numeric :$rel-tol is required,
                    Numeric :$abs-tol is required)

如果 $got$expected 数值彼此近似相等,则将测试标记为通过。子例程可以用多种方式调用,这些方式允许您使用不同值的相对公差 ($rel-tol) 或绝对公差 ($abs-tol) 进行测试。

如果没有设置公差,则该函数将基于 $expected绝对值来确定公差:如果它小于 1e-6,则使用 1e-5 的绝对公差;如果它更大,则使用 1e-6 的相对公差。

my Numeric ($value$expected$abs-tol$rel-tol= ...
 
is-approx $value$expected;
is-approx $value$expected'test description';
 
is-approx $value$expected$abs-tol;
is-approx $value$expected$abs-tol'test description';
 
is-approx $value$expected:$rel-tol;
is-approx $value$expected:$rel-tol'test description';
 
is-approx $value$expected:$abs-tol;
is-approx $value$expected:$abs-tol'test description';
 
is-approx $value$expected:$abs-tol:$rel-tol;
is-approx $value$expected:$abs-tol:$rel-tol'test description';

绝对公差§

当设置绝对公差时,它将用作第一个和第二个参数可以不同的实际最大值。例如

is-approx 342# success 
is-approx 362# fail 
 
is-approx 3003022# success 
is-approx 3004002# fail 
is-approx 3006002# fail 

无论给定什么值,它们之间的差值都不能大于 2

相对容差§

当设置相对容差时,测试将检查值之间的相对差值。给定相同的容差,给定的数字越大,它们可以相差的值就越大。

例如

is-approx 1010.5:rel-tol<0.1># success 
is-approx 1011.5:rel-tol<0.1># fail 
 
is-approx 100105:rel-tol<0.1># success 
is-approx 100115:rel-tol<0.1># fail 

两个版本都使用 0.1 作为相对容差,但第一个可以相差大约 1,而第二个可以相差大约 10。用于计算差值的函数是

              |value - expected|
⁣rel-diff = ────────────────────────
           max(|value|, |expected|)

如果 rel-diff 高于 $rel-tol,则测试将失败。

同时指定绝对容差和相对容差§

is-approx $value$expected:rel-tol<.5>:abs-tol<10>;

当同时指定绝对容差和相对容差时,将独立测试每个容差,并且只有当两个容差都通过时,is-approx 测试才会成功。

sub is-approx-calculate§

sub is-approx-calculate($got$expected$abs-tol where { !.defined or $_ >= 0 },
                        $rel-tol where { !.defined or $_ >= 0 }$desc)

这是当指定绝对容差和相对容差时 is-approx 调用的实际例程。它们独立测试,并且只有当两个测试都通过时,测试才会成功。

sub is-deeply§

multi is-deeply(Seq:D $gotSeq:D $expected$reason = '')
multi is-deeply(Seq:D $gotMu $expected$reason = '')
multi is-deeply(Mu $gotSeq:D $expected$reason = '')
multi is-deeply(Mu $gotMu $expected$reason = '')

如果第一个和第二个参数相等,则标记测试为通过,使用与 eqv 运算符 相同的语义。这是检查(深度)数据结构相等性的最佳方法。该函数接受最后一个参数作为测试的可选描述。

use Test;
plan 1;
 
sub string-info(Str() $_{
    Map.new: (
      length  =>  .chars,
      char-counts => Bag.new-from-pairs: (
          letters => +.comb(/<:letter>/),
          digits  => +.comb(/<:digit>/),
          other   => +.comb(/<.-:letter-:digit>/),
    ))
}
 
is-deeply string-info('42 Butterflies ♥ Raku'), Map.new((
    :21length,
    char-counts => Bag.new-from-pairs: ( :15letters, :2digits, :4other, )
)), 'string-info gives right info';

注意:由于 历史原因is-deeplySeq:D 参数通过调用 .cache 转换为 List。如果您想确保严格的 Seq 比较,请改用 cmp-ok $got, 'eqv', $expected, $desc

sub cmp-ok§

multi cmp-ok(Mu $got is raw$opMu $expected is raw$desc = '')

使用给定的 $op 比较器比较 $got$expected,如果比较产生 True 值,则通过测试。测试描述是可选的。

$op 比较器可以是 Callable 或包含中缀运算符(例如 '==''~~' 或用户定义的中缀)的 Str

cmp-ok 'my spelling is apperling''~~', /perl/"bad speller";

元运算符不能作为字符串给出;请将其作为 Callable 传递

cmp-ok <a b c>, &[!eqv], <b d e>'not equal';

Callable $op 允许您使用自定义比较

sub my-comp { $^a / $^b  < rand };
cmp-ok 1&my-comp2'the dice giveth and the dice taketh away'
cmp-ok 2-> $a$b { $a.is-prime and $b.is-prime and $a < $b }7,
    'we got primes, one larger than the other!';

sub isa-ok§

multi isa-ok(Mu $varMu $type$desc = "The object is-a '$type.raku()'")

如果给定的对象 $var 是给定 $type 的子类或继承自给定 $type,则标记测试为通过。为了方便,类型也可以指定为字符串。该函数接受测试的可选描述,默认为描述对象的字符串。

class Womble {}
class GreatUncleBulgaria is Womble {}
my $womble = GreatUncleBulgaria.new;
 
isa-ok $wombleWomble"Great Uncle Bulgaria is a womble";
isa-ok $womble'Womble';     # equivalent 

请注意,与 isa 不同,isa-ok 也匹配 Roles

say 42.isa(Numeric); # OUTPUT: «False␤» 
isa-ok 42Numeric;  # OUTPUT: «ok 1 - The object is-a 'Numeric'␤» 

sub can-ok§

multi can-ok(Mu $varStr $meth$desc = "..." )

如果给定的 $var 可以运行名为 $meth 的方法,则标记测试为通过。该函数接受可选描述。例如

class Womble {
    method collect-rubbish { ... }
}
my $womble = Womble.new;
 
# with automatically generated test description 
can-ok $womble'collect-rubbish';
#  => An object of type 'Womble' can do the method 'collect-rubbish' 
 
# with human-generated test description 
can-ok $womble'collect-rubbish'"Wombles can collect rubbish";
#  => Wombles can collect rubbish 

sub does-ok§

multi does-ok(Mu $varMu $type$desc = "...")

如果给定的 $var 可以执行给定的角色 $type,则标记测试为通过。该函数接受测试的可选描述。

# create a Womble who can invent 
role Invent {
    method brainstorm { say "Aha!" }
}
class Womble {}
class Tobermory is Womble does Invent {}
 
# ... and later in the tests 
use Test;
 
my $tobermory = Tobermory.new;
 
# with automatically generated test description 
does-ok $tobermoryInvent;
#  => The object does role Type 
 
does-ok $tobermoryInvent"Tobermory can invent";
#  => Tobermory can invent 

子例程 like§

sub like(Str() $gotRegex:D $expected$desc = "text matches $expected.raku()")

以这种方式使用

like 'foo', /fo/'foo looks like fo';

如果第一个参数(强制转换为字符串时)与指定为第二个参数的正则表达式匹配,则将测试标记为通过。该函数接受测试的可选描述,其默认值打印预期的匹配项。

子例程 unlike§

multi unlike(Str() $gotRegex:D $expected$desc = "text does not match $expected.raku()")

以这种方式使用

unlike 'foo', /bar/'foo does not look like bar';

如果第一个参数(强制转换为字符串时)与指定为第二个参数的正则表达式匹配,则将测试标记为通过。该函数接受测试的可选描述,其默认值为打印不匹配的文本。

子例程 use-ok§

multi use-ok(Str $code$desc = "$code module can be use-d ok")

如果给定的 $module 正确加载,则将测试标记为通过。

use-ok 'Full::Qualified::ModuleName';

由于 $code 正在转换为 EVAL,因此您还可以传递参数

use-ok 'Full::Qualified::ModuleName :my-argument';

子例程 dies-ok§

multi dies-ok(Callable $code$reason = '')

如果给定的 $code 抛出异常,则将测试标记为通过。

该函数接受测试的可选描述。

sub saruman(Bool :$ents-destroy-isengard{
    die "Killed by Wormtongue" if $ents-destroy-isengard;
}
 
dies-ok { saruman(ents-destroy-isengard => True}"Saruman dies";

子例程 lives-ok§

multi lives-ok(Callable $code$reason = '')

如果给定的 $code 抛出异常,则将测试标记为通过。

该函数接受测试的可选描述。

sub frodo(Bool :$destroys-ring{
    die "Oops, that wasn't supposed to happen" unless $destroys-ring;
}
 
lives-ok { frodo(destroys-ring => True}"Frodo survives";

子例程 eval-dies-ok§

multi eval-dies-ok(Str $code$reason = '')

如果给定的 $string 在作为代码 eval 时抛出异常,则将测试标记为通过。

该函数接受测试的可选描述。

eval-dies-ok q[my $joffrey = "nasty";
               die "bye bye Ned" if $joffrey ~~ /nasty/],
    "Ned Stark dies";

子例程 eval-lives-ok§

multi eval-lives-ok(Str $code$reason = '')

如果给定的 $string 在作为代码 eval抛出异常,则将测试标记为通过。

该函数接受测试的可选描述。

eval-lives-ok q[my $daenerys-burns = False;
                die "Oops, Khaleesi now ashes" if $daenerys-burns],
    "Dany is blood of the dragon";

子例程 throws-like§

sub throws-like($code$ex_type$reason?*%matcher)

如果给定的 $code 抛出特定的异常预期异常类型 $ex_type,则将测试标记为通过。代码 $code 可以指定为 Callable 或作为要 EVAL 的字符串。异常指定为类型对象。

如果抛出异常,它还将尝试匹配匹配器哈希,其中键是将在异常上调用的方法的名称,而值是它应该具有的通过值。例如

sub frodo(Bool :$destroys-ring{
    fail "Oops. Frodo dies" unless $destroys-ring
};
throws-like { frodo }Exceptionmessage => /dies/;

该函数接受测试的可选描述作为第三个位置参数。

该例程使 Failures 致命。如果您希望避免这种情况,请使用 no fatal pragma 并确保测试代码不会沉没可能的 Failures。如果您希望测试代码返回 Failure 而不是抛出,请改用 fails-like 例程。

sub fails-not-throws { +"a" }
# test passes, even though it's just a Failure and would not always throw: 
throws-like { fails-not-throws }Exception;
 
# test detects nothing thrown, because our Failure wasn't sunk or made fatal: 
throws-like { no fatalmy $ = fails-not-throwsNil }Exception;

请注意,仅当不引用周围作用域中的任何符号时,才能使用字符串形式(对于 EVAL)。如果引用了,则应使用块和 EVAL 封装字符串。例如

throws-like { EVAL q[ fac("foo") ] }X::TypeCheck::Argument;

子例程 fails-like§

sub fails-like ( \test where Callable:D|Str:D$ex-type$reason?*%matcher)

throws-like 相同的接口,但检查代码返回 Failure 而不是抛出。如果代码确实抛出或返回的 Failure 已被 处理,则将被视为测试失败。

fails-like { +"a" }X::Str::Numeric,
    :message(/'Cannot convert string to number'/),
    'converting non-numeric string to number fails';

子例程 subtest§

multi subtest(Pair $what)
multi subtest($desc&subtests)
multi subtest(&subtests$desc = '')

subtest 函数执行给定的块,通常包含多个测试,可能包括 plandone-testing,并在 plantodoskip 计数中算作一个测试。仅当块中的所有测试都通过时,它才会通过测试。该函数接受子测试的可选说明。

class Womble {}
 
class GreatUncleBulgaria is Womble {
    has $.location = "Wimbledon Common";
    has $.spectacles = True;
}
 
subtest {
    my $womble = GreatUncleBulgaria.new;
 
    isa-ok $womble,            Womble,             "Correct type";
    is     $womble.location,   "Wimbledon Common""Correct location";
    ok     $womble.spectacles,                     "Correct eyewear";
 
}"Check Great Uncle Bulgaria";

你还可以将说明作为第一个位置参数放置,或使用 Pair,其中说明为键,子测试的代码为值。这对于具有大主体的子测试很有用。

subtest 'A bunch of tests'{
    plan 42;
    ...
    ...
}
 
subtest 'Another bunch of tests' => {
    plan 72;
    ...
    ...
}

子例程 todo§

multi todo($reason$count = 1)

有时测试还没有准备好运行,例如某个功能可能尚未实现,在这种情况下,可以将测试标记为 todo。或者,特定功能仅在特定平台上运行 - 在这种情况下,可以在其他平台上 skip 测试。

$count 个测试标记为 TODO,并给出 $reason 说明原因。默认情况下,仅一个测试将被标记为 TODO。

sub my-custom-pi { 3 };
 
todo 'not yet precise enough';         # Mark the test as TODO. 
is my-custom-pi(), pi'my-custom-pi'# Run the test, but don't report 
                                       # failure in test harness. 

上面测试代码的结果将类似于

not ok 1 - my-custom-pi # TODO not yet precise enough
# Failed test 'my-custom-pi'
# at test-todo.rakutest line 7
# expected: '3.14159265358979'
#      got: '3'

请注意,如果你对 subtest 使用 todo,其中所有失败的测试都将自动标记为 TODO,并且不会计入你的原始 TODO 计数。

子例程 skip§

multi skip()
multi skip($reason$count = 1)

跳过 $count 个测试,并给出 $reason 说明原因。默认情况下,仅跳过一个测试。当运行测试(或测试)会导致死机时,请使用此类功能。

sub num-forward-slashes($arg{ ... } ;
 
if $*KERNEL ~~ 'linux' {
    is num-forward-slashes("/a/b"),             2;
    is num-forward-slashes("/a//b".IO.cleanup), 2;
}
else {
    skip "Can't use forward slashes on Windows"2;
}

请注意,如果你将测试标记为已跳过,还必须阻止该测试运行。

子例程 skip-rest§

sub skip-rest($reason = '<unknown>')

跳过剩余的测试。如果测试文件中的其余测试都因某种条件而失败,请使用此函数跳过它们,并提供一个可选的 $reason 说明原因。

my $locationsub womble { ... }...;
unless $location ~~ "Wimbledon Common" {
    skip-rest "We can't womble, the remaining tests will fail";
    exit;
}
 
# tests requiring functional wombling 
ok womble();
# ... 

请注意,skip-rest 需要设置 plan,否则 skip-rest 调用将抛出错误。请注意,skip-rest 不会退出测试运行。手动执行此操作,或使用条件语句以避免运行任何进一步的测试。

另请参见 plan :skip-all('...') 以完全避免运行任何测试,以及 bail-out 以中止测试运行并将其标记为失败。

子例程 bail-out§

sub bail-out ($desc?)

如果你已经知道测试将失败,则可以使用 bail-out() 退出测试运行

my $has-db-connection;
...
$has-db-connection  or bail-out 'Must have database connection for testing';

该函数中止当前测试运行,向测试框架发出失败信号。需要提供一个可选的退出原因。子例程将调用 exit(),因此如果您需要执行清理,请在调用 bail-out() 之前执行。

如果您想中止测试运行,但又不将其标记为失败,请参阅 skip-restplan :skip-all('...')

子例程 pass§

multi pass($desc = '')

pass 函数将测试标记为通过。flunk 将测试标记为通过。这两个函数都接受可选的测试描述。

pass "Actually, this test has passed";
flunk "But this one hasn't passed";

由于这些子例程不提供有关接收到的值和预期的值的信息,因此应谨慎使用它们,例如在评估复杂测试条件时。

子例程 flunk§

multi flunk($reason = '')

pass 的相反操作,使测试失败,并带有可选消息。

子例程 diag§

sub diag($message)

在标准错误流中以与 TAP 兼容的方式显示诊断信息。通常在特定测试无法提供测试本身未提供的信息时使用。或者,它可用于提供有关测试文件测试进度(在进行压力测试时非常重要)的可视标记。

diag "Yay!  The tests got to here!";

类型图§

Test 的类型关系
raku-type-graph Test Test Any Any Test->Any Mu Mu Any->Mu

展开上面的图表