Every app has that last inch (or mile) of code that’s not covered by tests. Usually it’s an interactive cycle of compile-run-inspect on the command line like

You Test

 curl -X POST https://reqbin.com/echo/post/json

##👀 You Expect:

{"success":"true"}

Despite having 3-4 testing frameworks for unit tests, e2e, regression etc– there’s always a gap where you find yourself re-playing commands in the terminal to test.

A common case is 🔥firefighting where ad-hoc tests are needed to validate an emergency config change or deployment.

Not only is this a waste of time, it’s error prone and reduces the number of assertions per run.

With Test::More, you can easily run dozens of assertions in < 1 second. Moreover, you can run them in a loop with watch perl test.t (read to the end to find out)

Close the Testing Gap with Perl

To close this Gap, you want a basic testing framework to easily test assertions on the command line: asserting expected output or return status (e.g. success == 0, failure >= 1)

Perl Test::More is an elegant solution:

  • it has a trivial and easy-to-remember interface: ok(), is(), isnt()
  • it easily tests shell scripts like head myfile.txt to test output, or system grep needle < haystack to test return code
  • is universally available on every distro. No apt-get, npm-install, downloading, linking, compiling needed.

Let’s Get Started

use Test::More tests => 1; # or

# basic test of stdout
is(`echo -n "hello dev.to"`, "hello dev.to", "test echo")  

The most basic is() test using actual backticks. First arg is the command, second arg is expected output, third is a test description.

Testing Return Status Code Success

# test success status code = 0
is((system "echo hello dude | grep -q dude"), 0, "test output contains dude");

Similar to above, but use (system “COMMAND”) to test the return status code instead of the output

Test For Failure / Status code = 1

In this case, grep for “dude”, assert that it fails (status=1)

# test failure status code = 1 (shift left by 8)
is((system "echo hello bob | grep -q dude"), 1<<8, "test output does not contain dude");

To test non-zero, bit-shift by 8 (read the docs for system to understand why)

Test File Contents

# test reading a file
is(`head -n 1 /etc/passwd`, "root:x:0:0:root:/root:/bin/bash\n", 'top of passwd')

This pattern is useful, using head , tail or grep to test for file content. Works well for testing log output upon running a command.

Testing APIs

# test curl output with special chars and newline
is(`curl -X POST https://reqbin.com/echo/post/json`, 
<<WANT
{"success":"true"}
WANT
, "test post req");

Use “heredoc” (multi-line doc) to test special characters & newlines without needing lots of escapes.

Run the Suite in the Background

use Test::More tests => 6; # or

# basic test of stdout
is(`echo -n "hello dev.to"`, "hello dev.to", "test echo");
# test success status code = 0
is((system "echo hello dude | grep -q dude"), 0, "test output contains dude");
# test failure status code = 1 (shift left by 8)
is((system "echo hello bob | grep -q dude"), 1<<8, "test output does not contain dude");
is((system "false"), 1<<8, "false not ok");
# test reading a file
is(`head -n 1 /etc/passwd`, "root:x:0:0:root:/root:/bin/bash\n", 'top of passwd');
# test curl output with special chars and newline
is(`curl -X POST https://reqbin.com/echo/post/json`, 
<<WANT
{"success":"true"}
WANT
, "test post req");

Now you have a suite of 6 tests, open a second terminal and run it with watch perl test.t

Every 2.0s: perl test.t                                                                                   pi4plus: Sat Aug 21 05:09:50 2021

1..6
ok 1 - test echo
ok 2 - test output contains dude
ok 3 - test output does not contain dude
ok 4 - false not ok
ok 5 - top of passwd
ok 6 - test post req

With Perl shell tests running in a loop, you can focus on code changes and your terminal will ring if a test case breaks.

Wrap Up

I hope this helps you close that last gap of code that’s not tested. Even in the middle of a fire, you can copy-paste your tests into a test.t file within the terminal.

Read the Perl Docs for More Info on Test::More & system