ciarand.me another code blog

Writing CLI unit tests in Perl

I was recently asked to write a small C script for reading some basic system stats. Nothing particularly exciting, and certainly not worth including any large unit testing frameworks for. Going without any tests at all (going commando) just doesn't feel right though. Instead, I decided to use Perl and its Test::Simple module to write some quick tests.

Here's our program (stored in script.c, compiled to script):

#include <stdio.h>

int main(int argc, const char* argv[]) {
    printf("argc: %d", argc);

    if (argc == 1) {
        printf("you called this script with no arguments\n");
    } else if (argc == 2) {
        printf("you called this script with 1 argument\n");
    } else {
        printf("you called this script with %d arguments\n", argc - 1);
    }

    return 0;
}

Easy enough, we can test this. Here's our test script:

#!/usr/bin/env perl -w

use strict;
use warnings;

use Test::Simple tests => 4;

ok(`./script` =~ /no arguments$/, "no args");
ok(`./script foo` =~ /1 argument$/, "1 arg");
ok(`./script foo bar` =~ /2 arguments$/, "2 args");
ok(`./script foo bar baz` =~ /3 arguments$/, "3 args");

Super simple. Let's go over the parts of the script.

#!/usr/bin/env perl -w

Perl's she-bang line. This just tells the kernel to run this script using the user's Perl bin with the -w (warn) flag on. Normally Perl let's you get away with a lot of nonsense, and the -w flag restricts the level of nonsense that's acceptable.

Note: this post originally stated that the she-bang line was for the shell. It's actually for the kernel. The More You Know™

use Test::Simple tests => 4;

Use the Test::Simple pod. The most important part here is the tests => 4, which is where we tell the script that we're planning to run 4 tests. That way it knows that if a different number of tests are run something went wrong and it'll tell us.

ok(`./script` =~ /no arguments$/, "no args")
ok(`./script foo` =~ /1 argument$/, "1 arg")
ok(`./script foo bar` =~ /2 arguments$/, "2 args")
ok(`./script foo bar baz` =~ /3 arguments$/, "3 args")

Ah, the meat of the script. The ok subroutine just tallies up a passed test if the first argument is true or a failed test if it evaluates to false. The second (optional) argument is a test title.

The backticks (`) around the ./script execute the command and return the stdout. The =~ just returns true if the regex on the right matches the string on the left.

Running the script gets us:

λ ./test.pl
1..4
ok 1 - no args
ok 2 - 1 arg
ok 3 - 2 args
ok 4 - 3 args

There, that's an easy (and lightweight) way of testing simple cli scripts.