Overriding Operators Everywhere

     

Overriding Perl operators locally is an important skill to know. Sometimes it's not sufficient, though. Consider the case of code that calls exit( ) occasionally. That's anathema to testing, but you don't have to give up on unit testing altogether. If you can isolate the affected code to a few places in the program, you can test that code in isolation, redefining the systemwide exit( ) function to do what you want.

How do I do that?

Take the example of a module that enforces password protection for users. Save the following code as PasswordKeeper.pm in your library directory:

 package PasswordKeeper;     sub new     {         my ($class, $username) = @_;         my $password           = $class->encrypt( $username );         bless         {             user     => $username,             tries    => 0,             password => $password,         }, $class;     }     sub verify     {         my ($self, $guess) = @_;         return 1 if $self->encrypt( $guess ) eq $self->{password};         $self->{tries}++;         exit if $self->{tries} =  = 3;         return 0;     }     sub encrypt     {         my ($class, $password) = @_;         return scalar reverse $password;     }     1; 


Note: Don't use this encryption technique for data you care about. See the Crypt namespace on the CPAN for better options .

That exit( ) looks a little dangerous, but at least it occurs in only one method. Save the following test file as pkeeper_exit.t :

 #!perl     use strict;     use warnings;     use lib 'lib';     use Test::More tests => 3;     my $exited;     BEGIN { *CORE::GLOBAL::exit = sub { $exited++ } };     my $module = 'PasswordKeeper';     use_ok( $module ) or die( "Could not load $module" );     my $mel = $module->new( 'Melanie' );     isa_ok( $mel, $module );     $mel->verify( $_ ) for qw( buffy babycat milkyway );     is( $exited, 1, 'verify(  ) should exit if it receives three bad passwords' ); 


Note: Assume that another test file exercises PasswordKeeper's non-exiting behavior .

Run it with prove :

 $  prove pkeeper_exit.t  pkeeper_exit...ok     All tests successful.     Files=1, Tests=3,  0 wallclock secs ( 0.07 cusr +  0.02 csys =  0.09 CPU) 

What just happened ?

PasswordKeeper works by taking a username and encrypting it to make a password when it creates a new object. The verify( ) method takes a potential password, encrypts it, and compares it against the stored password. If they match, the method returns true. Otherwise, it increases a counter of failed attempts and exits the program if someone has tried three unsuccessful passwords.


Note: Read perldoc perlsub and perldoc perlvar to learn more about CORE::GLOBAL. This is very powerful, so use it with care .

That exiting is important behavior to test. The test file starts by defining exit( ) in the special CORE::GLOBAL namespace. That overrides exit( ) everywhere , not just in main , where the code of the test file lives, or in PasswordKeeper . The new exit( ) increments the $exited variable, so the third test in the file can check that PasswordKeeper called exit( ) once for three failed password attempts.

What about...

Q:

What's the advantage of overriding something everywhere instead of in a small scope?

A:

You might not be able to localize all of the calls to exit( ) (or system( ) , die( ) , etc.) into one place in one module of the code you're testing. In those situations, overriding the troublesome operator in a single test file that exercises the behavior can turn previously difficult code into testable code.

Make this test file small, so that it exercises only the code paths that cause the exiting. This will minimize the chances of unexpected behavior from your global overriding. If you can't modify the code you're testing to make it easier to test, at least you can encapsulate the tricky code into individual test files.



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