views:

72

answers:

3

Situation:

I have a module Foo::Quux::Bar, living in ./Bar.pm. I need to be able to unit test Bar. However, it is not advantageous due to circumstances beyond my control to set up a Foo/Quux directory structure.

So what I'd like to do is have some sort of unit_test_use routine that lets me grab Bar.pm and move/copy its functions into the local namespace(Note that Bar has a package Foo::Quux::Bar specifier) for my testing pleasure.

Grubbing around in the Perl documentation has not helped me.

+4  A: 

The example below uses the following Bar.pm:

package Foo::Quux::Bar;

use warnings;
use strict;

sub one { 1 }

sub two { "zwei" }

sub three { 0x3333 }

1;

In your test-bar program, you can install a hook that will use the current directory's Bar.pm with

#! /usr/bin/perl

use warnings;
use strict;

use File::Basename;

BEGIN {
  sub find_bar {
    my(undef,$name) = @_;

    if (basename($name) eq "Bar.pm") {
      open my $fh, "<", "./Bar.pm" or die "$0: open ./Bar.pm: $!";
      $fh;
    }
  }

  unshift @INC => \&find_bar;
}

Hooks in @INC are documented in the perlfunc documentation for require.

Now to import all subs, ignoring any import in Foo::Quux::Bar,

# fake use Foo::Quux::Bar
BEGIN {
  require Foo::Quux::Bar;
  {
    no strict 'refs';
    while (my($name,$glob) = each %Foo::Quux::Bar::) {
      if (*{ $glob }{CODE}) {
        *{ __PACKAGE__ . "::" . $name } = *{ $glob }{CODE};
      }
    }
  }
}

Back out in the test code where the strict pragma is enabled, we can

print map "$_\n", one, two, three;

and get the following output:

1
zwei
13107
Greg Bacon
I took your solution's approach and brutally slammed the Foo::Quux::Bar symbol table into main's symbol table, allowing directly testing the functions in Bar. Many good coding practices died to bring me that information...
Paul Nathan
@Paul I'm not sure which is the appropriate response: you're welcome or I'm sorry. :-)
Greg Bacon
+5  A: 

Assuming your Bar.pm exports its functions in the standard way, you can load it with require and do the import manually:

BEGIN { 
    require 'Bar.pm';        # now the package Foo::Quux::Bar is set up
    Foo::Quux::Bar->import;
};

But it's definitely worth looking into setting up the directory structure in the standard way, if you can.

friedo
Put this in a `BEGIN{}` block and it will be just like a fake `use` statement.
mobrule
Good point about the `BEGIN`. I'll add that to the example.
friedo
You can just say `require Bar;` you don't need to say `'Bar.pm'`.
cjm
A: 

Here's what I wrote:

sub import_module_into_main
{
   my ($mod_name, $filename) = @_;

   require $filename;

   no strict;
   foreach my $var ( keys( %{$mod_name . "::"}))
   {
      $main::{$var} = ${$mod_name. "::"}{$var};
   }

}

Invoke with this: import_module_into_main("Foo::Quux::Bar", "Bar.pm").

Paul Nathan