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.
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;
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With