Testing Interactive Programs

     

Unfortunately for testers, lots of useful programs are more than modules, well- factored Perl programs, or shared libraries. They have user interfaces, take input from the keyboard, and even produce output to the screen.

It may seem daunting to figure out how to mock all of the inputs and outputs to test the program. Fortunately, there's a solution. Test::Expect allows you to run external programs, feeding them input and checking their output, all within your test files.

How do I do that?

Think back to your early programming days, when the canonical example of accepting user input was building a calculator. In Perl, you may have written something like simplecalc.pl :

 #!perl          use strict;     use warnings;          print "> ";          while (<>)     {         chomp;         last unless $_;              my ($command, @args) = split( /\s+/, $_ );              my $sub;         unless ($sub = _ _PACKAGE_ _->can( $command ))         {             print "Unknown command '$command'\n> ";             next;         }              $sub->(@args);         print "> ";     }          sub add     {         my $result = 0;              $result += $_ for @_;         print join(" + " , @_ ), " = $result\n";     }          sub subtract     {         my $result = shift;              print join(" - " , $result, @_ );              $result -= $_ for @_;         print " = $result\n";     } 

Save the file and play with it. Enter the commands add or subtract , followed by multiple numbers . It will perform the appropriate operation and display the results. If you give an invalid command, it will report an error. Enter a blank line to quit.

It's tempting to test this program with the technique shown earlier in "Writing Testable Programs," but the loop is central to the program and difficult to test. Alternately, what if your assignment were to write this code in another language? Fortunately, the same testing technique works for both possibilities.

Save the following test file as testcalc.t :

 #!perl          use strict;     use Test::More tests => 7;     use Test::Expect;          expect_run(         command => "$^X simplecalc.pl",         prompt  => '> ',         quit    => "\n",     );          expect(    'add 1 2 3',    '1 + 2 + 3 = 6',  'adding three numbers'       );     expect_send('subtract 1 2 3',                'subtract should work'       );     expect_is(  '1 - 2 - 3 = -4',                '.. producing good results'  );     expect_send('weird magic',                   'not dying on bad input'     );     expect_like(qr/Unknown command 'weird/,      '... but giving an error'    ); 

Run it from the directory containing simplecalc.pl :

 $  prove testcalc.t  testcalc....ok     All tests successful.     Files=1, Tests=7,  0 wallclock secs ( 0.27 cusr +  0.02 csys =  0.29 CPU) 

What just happened ?

The test file begins with a call to expect_run( ) to tell Test::Expect about the program to automate. The command argument provides the command to launch the program. In this case, it needs to launch simplecalc.pl with the currently executing Perl binary ( $^X ). The program's prompt is " > ", which helps the module know when the program awaits input. Finally, the quit argument contains the sequence to end the program.


Note: Test::Expect works like the Expect automation tool, which also has Perl modules in the form of Expect. pm and Expect:: Simple .

The first test calls expect( ) , passing the command to send to the program and the output expected from the program. If those match, the test passes ”actually twice, once for being able to send the data to the program correctly and the second time for the actual results matching the expected results.

The next test uses expect_send( ) to send data to the program. Though there's nothing to match, this test passes if the program accepts the input and returns a prompt.

Now that the program has sent some data, the test can check the results of the last operation by calling expect_is( ) to match the expected data directly. It works just like Test::More::is( ) , except that it takes the received data from the program run through Test::Expect , not from an argument to the function.

The expect_like( ) function is similar. It applies a regular expression to the data returned from the last operation performed by the program.

What about...

Q:

That's pretty simple, but I need to use more prompts and handle potential errors. What can I do?

A:

Test::Expect uses Expect::Simple internally. The latter module provides more options to drive external programs. You may have to use Test::More::is( ) and Test::More::like( ) to perform comparisons, but Expect::Simple handles the messy work of connecting to and driving an external program.



Perl Testing. A Developer's Notebook
Perl Testing: A Developers Notebook
ISBN: 0596100922
EAN: 2147483647
Year: 2003
Pages: 107

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net