测试代码是软件开发中不可或缺的一部分。测试提供对代码行为的自动化、可重复验证,并确保你的代码按预期工作。

在 Raku 中,Test 模块提供了一个测试框架,Raku 的官方 spectest 套件也使用该框架。

测试函数会发出符合 Test Anything Protocol 的输出。通常,它们在 sink 上下文中使用

ok check-name($meta:$relaxed-name), "name has a hyphen rather than '::'"

但是,如果测试成功,所有函数也会返回一个布尔值,该值可用于在测试失败时打印一条消息

ok check-name($meta:$relaxed-name), "name has a hyphen rather than '::'" \
  or diag "\nTo use hyphen in name, pass :relaxed-name to check-name\n";

编写测试§

虽然你可以以不同的方式组织你的测试,但典型的 Raku 约定是将测试放在项目基本目录中的 t 目录下。

一个典型的测试文件看起来像这样

use Test;      # a Standard module included with Rakudo 
use lib 'lib';
 
plan $num-tests;
 
# .... tests 
 
done-testing;  # optional with 'plan' 

我们加载内置的 Test 模块,并指定其他库的位置。然后,我们指定我们计划运行的测试数量(以便测试框架可以告诉我们运行的测试数量是否多于或少于我们的预期),并在完成测试后,我们使用 done-testing 告诉框架我们已完成。

线程安全性§

请注意,Test 模块中的例程不是线程安全的。这意味着你不应该尝试在多个线程中同时使用测试例程,因为 TAP 输出可能会乱序,并使解释它的程序感到困惑。

目前没有使其线程安全。如果线程测试对你至关重要,你可能会发现一些合适的 生态系统模块 来代替 Test 来满足你的测试需求。

运行测试§

可以通过在命令行上指定测试文件名来单独运行测试

$ raku t/test-filename.rakutest

要递归运行目录中的所有测试,可以使用 prove6 应用程序。

在使用 zef 之前,你必须安装它

$ zef install App::Prove6

你可以这样在一个发行目录中运行 prove6

$ prove6 --lib t/

t/ 参数指定包含测试的目录,--lib 选项用于将 lib 目录包含到 Raku 发行路径中,它等同于 raku 命令的 -Ilib 参数。

有关 prove6 用法的更多文档,请参阅 其页面

若要中止第一个失败的测试套件,请设置RAKU_TEST_DIE_ON_FAIL环境变量

$ RAKU_TEST_DIE_ON_FAIL=1 raku t/test-filename.rakutest

可以在测试文件中使用同一个变量。在加载Test模块之前设置它

BEGIN %*ENV<RAKU_TEST_DIE_ON_FAIL> = 1;
use Test;
...

注意:在 Rakudo 2020.05 版本之前,环境变量 PERL6_TEST_DIE_ON_FAIL 用于启用此功能,它仍然受支持,但已弃用。

可以通过设置RAKU_TEST_TIMES环境变量以微秒为单位输出测试时间

$ env RAKU_TEST_TIMES=1 raku -e 'use Test; plan 1; pass sleep(1);'
1..1
# between two timestamps 0 microseconds
ok 1 -
# t=1000721

可以在测试文件中使用同一个变量。在加载Test模块之前设置它

BEGIN %*ENV<RAKU_TEST_TIMES> = 1;
use Test;
...

注意:在 Rakudo 2020.05 版本之前,环境变量 PERL6_TEST_TIMES 用于启用此功能,它仍然受支持,但已弃用。

测试计划§

测试计划使用plan来声明将要执行或可能跳过的计划数。如果未声明计划,则使用done-testing来声明测试结束。

测试返回值§

Test模块导出各种函数,这些函数检查给定表达式的返回值并生成标准化的测试输出。

在实践中,该表达式通常是对要进行单元测试的函数或方法的调用。oknok将匹配TrueFalse。但是,在可能的情况下,最好使用以下专门的比较测试函数之一,因为如果比较失败,它们可以打印更有用的诊断输出。

通过字符串比较§

isisnt使用适当的操作符测试相等性,具体取决于处理的对象(或类)。

通过近似数值比较§

is-approx以一定的精度(可以是绝对精度或相对精度)比较数字。对于精度取决于内部表示的数值,它可能很有用。

通过结构比较§

还可以使用is-deeply比较结构,它将检查所比较对象的内部结构是否相同。

通过任意比较§

你可以使用cmp-ok进行任何类型的比较,它将你希望用于比较的函数或操作符作为参数。

通过对象类型§

isa-ok测试对象是否属于特定类型。

通过方法名称§

can-ok用于检查对象是否具有特定方法。

按角色§

does-ok 检查给定变量是否可以执行某个 角色

按正则表达式§

likeunlike 使用正则表达式进行检查;在第一种情况下,如果存在匹配项,则通过,在第二种情况下,如果不存在匹配项,则通过。

测试模块§

模块使用 use-ok 临时加载,如果加载失败,则会失败。

测试异常§

dies-oklives-ok 是测试代码的相反方式;第一个检查它是否抛出异常,第二个检查它是否不抛出异常;throws-like 检查代码是否抛出作为参数传递给它的特定异常;fails-like 以类似的方式检查代码是否返回特定类型的 Failureeval-dies-okeval-lives-ok 对在测试之前求值的字符串以类似的方式工作。

分组测试§

一组子测试的结果只有在所有子测试都为 ok 时才为 ok;它们使用 subtest 进行分组。

跳过测试§

有时测试还没有准备好运行,例如某个功能可能尚未实现,在这种情况下,测试可以标记为 todo。或者,给定功能可能只适用于特定平台 - 在这种情况下,可以在其他平台上 skip 测试;skip-rest 将跳过剩余的测试,而不是跳过作为参数给定的特定数量;bail-out 将简单地退出测试并显示一条消息。

手动控制§

如果上面记录的便利功能不适合你的需求,你可以使用以下函数手动指导测试工具输出;pass 将表示测试已通过,diag 将打印一条(可能)信息丰富的消息。