Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override variables while testing a standalone Perl script

Tags:

testing

perl

There is a Perl script in our environment that I now need to maintain. It is full of bad practices, including using (and re-using) global variables throughout the script. Before I start making changes to the script, I was going to try to write some test scripts so I can have a good regression base. To do this, I was going to use a method described on this page.

I was starting by writing tests for a single subroutine. I put this line somewhat near the top of the script I am testing:

return 1 if ( caller() );

That way, in my test script, I can

require 'script_to_test.pl';

and it won't execute the whole script.

The first subroutine I was going to test makes a lot of use of global variables that are set throughout the script. My thought was to try to override these variables in my test script, something like this:

require_ok('script_to_test.pl');
$var_from_other_script = 'Override Value';
ok( sub_from_other_script() );

Unfortunately (for me), the script I am testing has a massive "my" block at the top, where it declares all variables used in the script. This prevents my test script from seeing/changing the variables in the script I'm running tests against.

I've played with Exporter, Test::Mock..., and some other modules, but it looks like if I want to be able to change any variables I am going to have to modify the other script in some fashion.

My goal is to not change the other script, but to get some good tests running so when I do start changing the other script, I can make sure I didn't break anything. The script is about 10,000 lines (3,000 of them in the main block), so I'm afraid that if I start changing things, I will affect other parts of the code, so having a good test suite would help.

Is this possible? Can a calling script modify variables in another script declared with "my"?


And please don't jump in with answers like, "Just re-write the script from scratch", etc. That may be the best solution, but it doesn't answer my question, and we don't have the time/resources for a re-write.

like image 316
BrianH Avatar asked Apr 13 '10 14:04

BrianH


2 Answers

If you want to keep the variables lexical (if there are closures built with them) you can use the module PadWalker to poke around.

include something like this in the old code:

package somepackage;

use PadWalker qw/peek_my/;

my $x = 1;
# big my block declaration...

our $lexpad = peek_my 0;

then in your test code:

${ $somepackage::lexpad->{'$x'} } = 2;
like image 102
Eric Strom Avatar answered Oct 12 '22 04:10

Eric Strom


If the script has a package declaration (or if you can add one without changing the behavior of the script), then you can change the my declaration to an our declaration, and change variables using the fully qualified variable name.

Old script:

my($a,@b,$c,%d);

Change to:

package Some::Package;
our($a,@b,$c,%d);

And in your test script:

sub_from_other_script();
$Some::Package::c = 42;
$Some::Package::d{$key} = $value;
sub_from_other_script();
like image 32
mob Avatar answered Oct 12 '22 02:10

mob