Writing and running tests in Raku
Testing code is an integral part of software development. Tests provide automated, repeatable verifications of code behavior, and ensures your code works as expected.
In Raku, the Test module provides a testing framework, used also by Raku's official spectest suite.
The testing functions emit output conforming to the Test Anything Protocol. In general, they are used in sink context:
ok check-name(, :), "name has a hyphen rather than '::'"
but all functions also return as a Boolean if the test has been successful or not, which can be used to print a message if the test fails:
ok check-name(, :), "name has a hyphen rather than '::'" \or diag "\nTo use hyphen in name, pass :relaxed-name to check-name\n";
As with any Perl project, the tests live under the
t directory in the project's base directory.
A typical test file looks something like this:
use Test; # a Standard module included with Rakudouse lib 'lib';plan ;# .... testsdone-testing; # optional with 'plan'
We ensure that we're using Raku, via the
use v6.c pragma, then we load the
Test module and specify where our libraries are. We then specify how many tests we plan to run (such that the testing framework can tell us if more or fewer tests were run than we expected) and when finished with the tests, we use done-testing to tell the framework we are done.
Note that routines in
Test module are not thread-safe. This means you should not attempt to use the testing routines in multiple threads simultaneously, as the TAP output might come out of order and confuse the program interpreting it.
There are no current plans to make it thread safe. If threaded-testing is crucial to you, you may find some suitable ecosystem modules to use instead of
Test for your testing needs.
Tests can be run individually by specifying the test filename on the command line:
$ raku t/test-filename.t
To run all tests in the directory recursively, prove6 application can be used.
You have to install it before using with zef:
$ zef install App::Prove6
You can run
prove6 in a distribution directory this way:
$ prove6 --lib t/
t/ argument specified directory that contains tests and the
--lib option is passed to include
lib directory into Raku distribution path, it is an equivalent of
-Ilib argument of
For more documentation regarding
prove6 usage refer to its page.
To abort the test suite upon first failure, set the
RAKU_TEST_DIE_ON_FAIL environmental variable:
$ RAKU_TEST_DIE_ON_FAIL=1 raku t/test-filename.t
The same variable can be used within the test file. Set it before loading the
BEGIN <RAKU_TEST_DIE_ON_FAIL> = 1;use Test;...
Test module exports various functions that check the return value of a given expression and produce standardized test output.
In practice, the expression will often be a call to a function or method that you want to unit-test.
nok will match
False. However, where possible it's better to use one of the specialized comparison test functions below, because they can print more helpful diagnostics output in case the comparison fails.
is-approx compares numbers with a certain precision, which can be absolute or relative. It can be useful for numeric values whose precision will depend on the internal representation.
Structures can be also compared using
is-deeply, which will check that internal structures of the objects compared is the same.
You can use any kind of comparison with
cmp-ok, which takes as an argument the function or operator that you want to be used for comparing.
isa-ok tests whether an object is of a certain type.
can-ok is used on objects to check whether they have that particular method.
Modules are tentatively loaded with
use-ok, which fails if they fail to load.
lives-ok are opposites ways of testing code; the first checks that it throws an exception, the second that it does not;
throws-like checks that the code throws the specific exception it gets handed as an argument;
fails-like, similarly, checks if the code returns a specific type of Failure.
eval-lives-ok work similarly on strings that are evaluated prior to testing.
The result of a group of subtests is only
ok if all subtests are
ok; they are grouped using
Sometimes tests just aren't ready to be run, for instance a feature might not yet be implemented, in which case tests can be marked as
todo. Or it could be the case that a given feature only works on a particular platform - in which case one would
skip the test on other platforms;
skip-rest will skip the remaining tests instead of a particular number given as argument;
bail-out will simply exit the tests with a message.
If the convenience functionality documented above does not suit your needs, you can use the following functions to manually direct the test harness output;
pass will say a test has passed, and
diag will print a (possibly) informative message.