While writing unit tests for a module I ran across a few challenges...

Given a module like:

package Module::Under::Test;

sub method1 {
     ...
     $self->method2( ... );
     ...
}


To properly unit test this I want to replace method2 with a mock method 
so that I am only testing the actions of method1. The question is, how 
do you inject an overridden method2, aside from subclassing 
Module::Under::Test, which introduces other concerns (see below).

What I'd ideally like to do is take an instantiated Module::Under::Test 
object, and dynamically remap that instance to point to a different 
method2. Something like:

     my $mut = Module::Under::Test->new();
     $mut->method2 = sub { ... };

which of course wouldn't work. A typeglob could be used here:
     *Module::Under::Test::method2 = sub { ... };

but that alters the class, rather than the instance, and could impact 
other methods in the unit test. (I suppose one could save the current 
value and restore it later.)

My understanding is that Perl's OO dereferencing for methods doesn't 
involve the object instance, other than to obtain the class name so it 
can find the right package namespace. If this is correct, then there 
probably isn't a way to do this other than subclassing.

This kind of thing is easy to do in JavaScript:
     var mut = new Module.Under.Test();
     mut.method2 = function () { ... };
but in JavaScript there aren't real classes - everything is an instance. 
Still, it'd be a shame to think JavaScript could do something Perl 
can't. :-)



Another issue is how to simulate a "private" package namespace. For 
example, lets say you go the subclass route mentioned above, and in your 
test class create a test method like:

sub test_method1 {
     ...
     {
         package My::Module::Under::Test;
         use base qw(Module::Under::Test);
         sub method2 { ... }
     }

     my $mut = My::Module::Under::Test->new();
     ...
}


While you can declare a package in this fashion, the 
My::Module::Under::Test package has global visibility. To minimize the 
possibility of conflicts with other similar subclasses declared in other 
test methods, you'd ideally want to do something like:

         package __PACKAGE__::__SUB__::Module::Under::Test;

But this doesn't work. As documented, __PACKAGE__ doesn't get 
interpolated. It has to be by itself. __SUB__, although I found 
references to it on Usenet, doesn't seem to actually exist.

The easy solution here is to perform the substitution manually as you 
write the code, but it'd be nice if there was a more automated approach.

It seems the only option is to do a string eval like:

     my $PACKAGE = __PACKAGE__;
     eval "package ${PACKAGE}::Module::Under::Test;" . q{
         use base qw(Module::Under::Test);
         sub method2 { ... }
     };

(Similarly caller() could be used to provide the equivalent of what the 
hypothetical __SUB__ would have provided.)

Which works, but being a string eval this carries the down side of 
deferring syntax checking until run time, which is less than ideal.

Maybe this would work:

     {
         eval "package " . __PACKAGE__ . "::Module::Under::Test;";
         use base qw(Module::Under::Test);
         sub method2 { ... }
     }

but I haven't tried it. I would expect not, as method2 will get added to 
the symbol table at compile time before the package namespace gets set.

So does anyone know of hacks for dynamically generating a package 
namespace string?

Is there any way to create an anonymous class? (Which is apparently 
possible in Java.) Given that everything has to end up in the global 
symbol table, I'm guessing not.

Will any of this stuff be addressed in Perl 6?

  -Tom

-- 
Tom Metro
Venture Logic, Newton, MA, USA
"Enterprise solutions through open source."
Professional Profile: http://tmetro.venturelogic.com/
 
_______________________________________________
Boston-pm mailing list
Boston-pm@mail.pm.org
http://mail.pm.org/mailman/listinfo/boston-pm

Reply via email to