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, orsystem 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
Top comments (0)