TL;DR

Test::CLI helps doing tests through command-line applications.

Most of the time in my amateur programmer time I wrote tests to do some exercising of the programs I was working on. Sometimes, though, it so happens that I trust the programs, but I want to use them to test the environment a bit.

This is (also) where Test::CLI can come handy.

As an example, suppose you want to make an automated test that some virtual machines are active in your Openstack project. To get the list, you would run:

$ openstack server list
+-------+--------+----------+--------------+-------+--------+
| ID    | Name   | Status   | Networks     | Image | Flavor |
+-------+--------+----------+--------------+-------+--------+
| 01... | bar-00 | ACTIVE   | foo=10.0.1.1 |       | bar    |
| 23... | bar-01 | ACTIVE   | foo=10.0.1.2 |       | bar    |
| 45... | bar-02 | ACTIVE   | foo=10.0.1.3 |       | bar    |
| 67... | bar-03 | ACTIVE   | foo=10.0.1.4 |       | bar    |
| 89... | bar-04 | ACTIVE   | foo=10.0.1.5 |       | bar    |
| ab... | bar-05 | SHUTDOWN | foo=10.0.1.6 |       | bar    |
| cd... | bar-06 | ACTIVE   | foo=10.0.1.7 |       | bar    |
| ef... | baz-00 | ACTIVE   | foo=10.0.1.8 |       | baz    |
+-------+--------+----------+--------------+-------+--------+

This command should succeed and provide an output like the above example… except that bar-05 should be in ACTIVE state, not SHUTDOWN.

With Test::CLI, you might do the following:

use Test::More;
use Test::CLI 'tc';
my $tc = tc(qw< openstack server list >);
$tc->run_ok;
my @machines = map { sprintf 'bar-%02d', $_ } 0 .. 6;
push @machines, 'baz-00';
$tc->stdout_like(qr{\s $_ [\s|]+ ACTIVE \s}mxs) for @machines;
done_testing();

Running this program gives us:

$ perl test.pl 
ok 1 - openstack server list
ok 2 - (stdout match (?^umsx:\s bar-00 [\s|]+ ACTIVE \s) on) openstack server list
ok 3 - (stdout match (?^umsx:\s bar-01 [\s|]+ ACTIVE \s) on) openstack server list
ok 4 - (stdout match (?^umsx:\s bar-02 [\s|]+ ACTIVE \s) on) openstack server list
ok 5 - (stdout match (?^umsx:\s bar-03 [\s|]+ ACTIVE \s) on) openstack server list
ok 6 - (stdout match (?^umsx:\s bar-04 [\s|]+ ACTIVE \s) on) openstack server list
not ok 7 - (stdout match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s) on) openstack server list
# Failed test '(stdout match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s) on) openstack server list'
# at ../lib/Test/CLI.pm line 165.
# stdout: did not match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s)
ok 8 - (stdout match (?^umsx:\s bar-06 [\s|]+ ACTIVE \s) on) openstack server list
ok 9 - (stdout match (?^umsx:\s baz-00 [\s|]+ ACTIVE \s) on) openstack server list
1..9
# Looks like you failed 1 test of 9.

This can be of course be put in a harness:

$ prove -v test.pl 
test.pl .. 
ok 1 - openstack server list
ok 2 - (stdout match (?^umsx:\s bar-00 [\s|]+ ACTIVE \s) on) openstack server list
ok 3 - (stdout match (?^umsx:\s bar-01 [\s|]+ ACTIVE \s) on) openstack server list
ok 4 - (stdout match (?^umsx:\s bar-02 [\s|]+ ACTIVE \s) on) openstack server list
ok 5 - (stdout match (?^umsx:\s bar-03 [\s|]+ ACTIVE \s) on) openstack server list
ok 6 - (stdout match (?^umsx:\s bar-04 [\s|]+ ACTIVE \s) on) openstack server list
not ok 7 - (stdout match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s) on) openstack server list

# Failed test '(stdout match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s) on) openstack server list'
# at ../lib/Test/CLI.pm line 165.
# stdout: did not match (?^umsx:\s bar-05 [\s|]+ ACTIVE \s)
ok 8 - (stdout match (?^umsx:\s bar-06 [\s|]+ ACTIVE \s) on) openstack server list
ok 9 - (stdout match (?^umsx:\s baz-00 [\s|]+ ACTIVE \s) on) openstack server list
1..9
# Looks like you failed 1 test of 9.
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/9 subtests 

Test Summary Report
-------------------
test.pl (Wstat: 256 Tests: 9 Failed: 1)
  Failed test:  7
  Non-zero exit status: 1
Files=1, Tests=9,  0 wallclock secs ( 0.03 usr  0.00 sys +  0.09 cusr  0.00 csys =  0.12 CPU)
Result: FAIL

I think this is it!