How to write unit test cases for a Perl script

2.6k views Asked by At

I'm able to write unit tests test_case.t for a Perl Module ModuleOne.pm

test_case.t

use strict;
use warnings;
use Test::More;
use Test::Cmd;
use ModuleOne; # Included the module here

my $ret = ModuleOne::methodone(args);
is($ret->{val}, 1, "Checking return value"); # success 

I'm trying the achieve the same unit test cases for a perl script script_one.pl

script_one.pl

use strict;
use warnings;
use ModuleOne;

my $NAME;
my $ID;

# get parameters
GetOptions (
 "name" => \$NAME,
 "emp_id" => \$ID,
)

validate_params();

sub validate_params {
   # this method will validate params 
}

sub print_name {
   # this method will print name 
}

How can I include this perl file script_one.pl in test_case.t and write test cases for methods validate_params and print_name?

1

There are 1 answers

1
simbabque On BEST ANSWER

There are a couple of options. One is to use Test::Script to see if your code compiles and runs, and does some stuff. It's more of an integration test than a unit test though, and if you have external dependencies like writing to the file system, it's tough to mock those away like this.

Since you've got subs in the script, the easiest way is probably to require or do the script in your test file, maybe inside a different package (but that doesn't really matter). You can then call those functions, because they are in one of your namespaces.

use strict;
use warnings;
use Test::More;

package Foo {
    do 'script_one.pl';
};

is Foo::print_name, 'foo', 'prints the right name';

This way you can mock dependencies more easily and you get some more control. The only thing that might be tricky is code that's not in subs and will be run at invocation, like the call to validate_params. You could just use Capture::Tiny to brush that under the rug.

The best option though is to not have functions in your script. Just make another module that has those functions, and call it in your script. It's fine to have a script like the following.

#!/usr/bin/env perl
use strict;
use warnings;
use My::Modules::Foo;

My::Modules::Foo->run; # or ::run()

It doesn't matter if it's OOP or not. The idea will be the same. If you encapsulate it properly, you can unit-test all your code without ever using that script.

Regarding the GetOpts stuff, those variables can be lexicals to the script, but your naming with the capital letters and the lack of arguments to the validate_params call indicate that they are really package-wide and are used inside the function. Don't do that. Use arguments to the subs. Put all the subs in a package, then have GetOpts in the script, and pass the options as arguments to the function.

That way you can test everything and really don't need the script.