Write devopsish tests using Tomty and Raku

In this post I am going to give a quick introduction into Tomty.


Tomty is installed as Raku module, using zef - Raku package manager:

zef install --/test https://github.com/melezhik/Sparrow6.git
zef install --/test https://github.com/melezhik/Tomty.git

We need bleeding edge version of Tomty and Sparrow (dependency for Tomty) to get an access to all the features mentioned in this post.

Setup Tomty

Working with Tomty requires some initialization. First of all we need to create a working directory for tests and initialize it. Optionally we can install Bash completion for tomty - Tomty cli application:

export SP6_REPO=https://sparrowhub.io/repo
mkdir work
cd work
tomty --init
tomty --completion

Create first scenario

Scenarios could be written on many languages, in this post I'll focus on Bash as it's a simple and quite popular scripting language for devops tasks and automation. Tomty allows effectively right low level tasks in Bash while gluing them together with Raku.

A Bash scenario should be just a file named task.bash and located at some directory:

mkdir -p tasks/task1/
nano tasks/task1/task.bash
echo "hello world"

Once bash task is created, we can run it through a Raku wrapper:

tomty --edit my-task
task-run "tasks/task1"

In context of Raku a Bash task gets called as a Raku function task-run, with a parameter setting a task directory.

The approach gives us a benefit of using high level language to call low level Bash scenarios.

Now, let's run our first task:

tomty my-task
[repository] :: index updated from file:///root/repo/api/v1/index
[tasks/task1] :: hello world

The task executed successfully, now let's convert this trivial hello world example it into something more practical.

Check if a web site is available

The task of checking an http resource health check is quite common in daily devops @job. Let's see how we could do this in Tomty.

First, let's create a Bash task.

nano tasks/task1/task.bash
curl -s -f $(config url) -o /dev/null -D - | head  -n 4

The task is just using curl command to GET some http URL, pay attention that -f flag causes curl to exit with none zero exit code in case of receiving none successful http response.

A Tomty scenario, would change a bit to pass tested URL as a paramater into a Bash task:

tomty --edit my-task
task-run "tasks/task1", %(
  url => "https://raku.org"

Now let's see how it works:

tomty my-task
[tasks/task1] :: HTTP/2 200
[tasks/task1] :: date: Fri, 02 Jul 2021 18:20:58 GMT
[tasks/task1] :: content-type: text/html
[tasks/task1] :: last-modified: Tue, 25 May 2021 16:30:03 GMT

As we could see Bash config name is used as function to access named parameters passed to Bash from Raku as a Raku Hash. This semantic works because Tomty is build on top of Sparrow automation tool, that provides all type of such functionality.

Many times, relying on a script exit code to check if a test passes or not works just fine, however sometime one needs to specify expected script output to define a test state.

Let's see how Tomty allows that.

Task checks

Task checks allow define an output a test expects to see in a running script:

nano tasks/task1/task.check
HTTP/2 200

So we check that https://raku.org web server servers HTTP v2 protocol:

tomty my-task
[repository] :: index updated from file:///root/repo/api/v1/index
[tasks/task1] :: HTTP/2 200
[tasks/task1] :: date: Fri, 02 Jul 2021 19:08:34 GMT
[tasks/task1] :: content-type: text/html
[tasks/task1] :: last-modified: Tue, 25 May 2021 16:30:03 GMT
[task check] stdout match <HTTP/2 200> True

And if we want to check against v1 or v2, we can use Raku regular expressions:

nano tasks/task1/task.check
regexp: "HTTP/" 1 || 2 " 200"

Refer to Sparrow task check documentation to get full explanation of the DSL and see more complex examples of validating scripts output.


Once you have more and more tests you can run all then by using --all parameter of tomty cli:

tomty --all

However when there are too many tests, it's gets really difficult to manage them.

Tags allows to split your tests by multiple groups and run them specifically. It's could extremely useful with a huge tests base.

Let me show a quick simple example using the previous web site check scenario.

Say, we don't want to run our web site availability test if we don't have the internet access on our testing environment.

Let's first add a tag to a Raku scenario:

tomty --edit my-task

=begin tomty
  tags => [ 'offline']
=end tomty

task-run "tasks/task1", %(
  url => "https://raku.org"

As we can see tags added into dedicated "tomty" Pod6 sections as Raku array ( there could be many tags ).

Now let's see how we could use this tag, when running tomty cli:

tomty --skip=offline
[1/1] / [my-task] ....... SKIP
(=: / [1] tests in 0 sec / (0) tests passed

As we can see Tomty did not execute the test, as we required to skip any tests tagged as offline

We could have build even more sophisticated examples. For example to run only offline tests for application version 2, but to skip production tests:

tomty --only=offline+app-v2 --skip=production

To list all available tags run:

tomty --list --tags

Sometimes you want only list tests with theirs tags, not execute them, the previous example would be rewritten as:

tomty --tags --only=offline+app-v2 --skip=production


This was a brief introduction into Tomty - test framework written on Raku and supporting many scripting languages to write low level tasks. The main philosophy of Sparrow ( the engine Tomty is built upon ) - use those languages that fits your domain best ( in our case - Bash is simple and efficient way to send http requests) and glue scripts together using high language - Raku, where scripts get called as functions.

When I develop tests for my daily devops tasks on Tomty, I switch between Bash and Raku often and this approach allows me do my work effectively and with minimal efforts.

Thanks for reading. I'd like to hear feedback as usual.