4

I have inherited a perl code base. Consider the following subroutine;

sub getSysRTable
{
    my $iface = shift;
    return if not length($iface);
    my %ip_routes;
    my @routes = `/usr/bin/netstat -rn`;
    foreach my $route(@routes) {
        if ($route =~ /([\S.]+)\s+([\d.]+.[\d.]+.[\d.]+.[\d.]+)\s+(UGS|UGHS)\s+($iface)/ )
            { $ip_routes {$1} = $2 }
    }
    return %ip_routes;
}

I want to write unit tests for this code. The testing I have in mind will use sample output from netstat -rn and check for expected behaviour. The sub as is, invokes a command, so injecting my test data is problematic with this implementation.

What is the idiomatic perlish approach to refactoring this sub for testability?

Jev Björsell
  • 891
  • 1
  • 9
  • 25

1 Answers1

5

First, change your code as follows:

sub getDataForSysRTable {
    return `/usr/bin/netstat -rn`;
}

sub getSysRTable
{
    my $iface = shift;
    return if not length($iface);
    my %ip_routes;
    my @routes = getDataForSysRTable();
    foreach my $route(@routes) {
        if ($route =~ /([\S.]+)\s+([\d.]+.[\d.]+.[\d.]+.[\d.]+)\s+(UGS|UGHS)\s+($iface)/ )
            { $ip_routes {$1} = $2 }
    }
    return %ip_routes;
}

Then for your test, you can do

local *getDataForSysRTable = sub { 
   ... return known data ...
};

my $ip_routes = getSysRTable($iface);
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 2
    For mocking subroutines I prefer to use http://search.cpan.org/~gfranks/Test-MockModule-0.11/lib/Test/MockModule.pm – bart May 10 '16 at 20:42