Testing
Writing and running tests in Perl 6
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 Perl 6, the Test module provides a testing framework, used also by Perl 6'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";
Writing tests
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 Perl 6, 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.
Thread safety
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.
Running tests
Tests can be run individually by specifying the test filename on the command line:
$ perl6 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/
The t/
argument specified directory that contains tests and the --lib
option is passed to include lib
directory into Perl 6 distribution path, it is an equivalent of -Ilib
argument of perl6
command.
For more documentation regarding prove6
usage refer to its page.
To abort the test suite upon first failure, set the PERL6_TEST_DIE_ON_FAIL
environmental variable:
$ PERL6_TEST_DIE_ON_FAIL=1 perl6 t/test-filename.t
The same variable can be used within the test file. Set it before loading the Test
module:
BEGIN <PERL6_TEST_DIE_ON_FAIL> = 1;use Test;...
Test plans
Tests plans use plan
for declaring how many plans are going to be done or, as might be the case, skipped. If no plan is declared, done-testing
is used to declare the end of the tests.
Testing return values
The 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. ok
and nok
will match True
and 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.
By string comparison
is
and nok
test for equality using the proper operator, depending on the object (or class) it's handled.
By approximate numeric comparison
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.
By structural comparison
Structures can be also compared using is-deeply
, which will check that internal structures of the objects compared is the same.
By arbitrary comparison
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.
By object type
isa-ok
tests whether an object is of a certain type.
By method name
can-ok
is used on objects to check whether they have that particular method.
By role
does-ok
checks whether the given variable can do a certain Role.
By regex
like
and unlike
check using regular expressions; in the first case passes if a match exists, in the second case when it does not.
Testing modules
Modules are tentatively loaded with use-ok
, which fails if they fail to load.
Testing exceptions
dies-ok
and 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-dies-ok
and eval-lives-ok
work similarly on strings that are evaluated prior to testing.
Grouping tests
The result of a group of subtests is only ok
if all subtests are ok
; they are grouped using subtest
.
Skipping tests
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.
Manual control
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.