Ben Tilly wrote:
> sub replace_sub_for_instance {
>     my ($object, $subroutine_name, $new_subroutine) = @_;
>     no strict 'refs';
>     my $old_subroutine = \&$subroutine_name
>         or die "Subroutine $subroutine_name not found";
>     my $object_name = refaddr($object);
>     *$subroutine_name = sub {
>         my $self = $_[0];
>         if (refaddr($self) eq $object_name) {
>             goto $new_subroutine;
>         }
>         else {
>             goto $old_subroutine;
>         }
>     };
> }

On first read of Greg's code I thought he was suggesting modifying the 
class under test to incorporate the extra decision logic, but taking a 
second look after seeing your take above, I see the idea was to 
externally inject a wrapper with instance detection logic. This could be 
a good approach for stubbing out a method in a class under test.


> Tom Metro wrote:
>> If I wanted to interject my own layer of indirection at the instance
>> level, and was willing to modify the class under test, I'd probably opt
>> for an AUTOLOAD method...
> 
> Also note that AUTOLOAD and inheritance do NOT play well together.
> That's another reason to avoid that solution.

I had that thought as well. Isn't there a workaround where your AUTOLOAD 
handler can explicitly hand off to the superclass AUTOLOAD?


>> If this is the only option, then...(creating a
>> subclass) in the test class is far preferable.
> 
> The main problem that I see with subclasses is what happens if there
> is code that checks ref($object) against some pattern.

While I've mentioned that I wanted to avoid changing the code under 
test, I'd make an exception if I found it doing that, which is rarely a 
good thing.


> If you know the code and know that that doesn't happen, then I'd
> strongly recommend subclassing.

Which is how I was planning to implement the tests. The problem, as I 
noted in the original message, is that I wanted to avoid potential 
namespace conflicts between various test methods that each will need to 
subclass the object under test. As I mentioned, this can be addressed 
simply by paying attention to the naming of the subclasses, but I was 
hoping to find a bit more automated solution where variables could be 
injected. (Your give_object_unique_subclass() function addresses this.)


>> Of course to pull this off your class needs to be a
>> subclass of Class::Unique.
> 
> This problem is fixable.
> 
> sub give_object_unique_subclass {
>     my $object = shift;
>     my $class = ref($object);
>     my $unique_class = $class . "::" . refaddr($object);
>     {
>         no strict 'refs';
>         @{ $unique_class . '::ISA' } = ( $class );
>     }
>     bless($object, $unique_class);
> }

Yes, of course...rebless the object. I forgot about that.

That's also a nice solution. Should get this stuff on CPAN.


> The only big drawbacks to this are that code that checks ref can
> break, and we might break code that depends on the stringification of
> $object.  (Think inside-out objects.)

Isn't there also a potential problem if the if something upstream of the 
reblessing saves a reference to the object, or is this true:

   use Scalar::Util 'refaddr';
   my $obj = {};
   my $before = refaddr($obj);
   my $newobj = bless($obj, 'foo');
   print "true\n" if $before eq refaddr($newobj);

Perl says it is. Makes sense, as although bless returns an object, it 
shouldn't create a new one.

(The man page for Scalar::Util[1] doesn't say what data type refaddr() 
returns, but the examples show what appears to be a decimal number, 
rather than a hex string, as I would have expected, so that should 
probably be a numeric test above.)

1. http://search.cpan.org/~gbarr/Scalar-List-Utils-1.19/lib/Scalar/Util.pm


>>      my $subclass = __PACKAGE__ . '::test_method1::Module::Under::Test';
>>      my $mut = Module::Under::Test::new($subclass);
>> [...]
>> But if new() is declared in a superclass, that breaks.
> 
> New being declared in a superclass isn't a problem. 

Are you sure? I thought these:
   Module::Under::Test->new
   new Module::Under::Test
would cause a search for new() in the class hierarchy, but
   Module::Under::Test::new
will only try to run new() in package Module::Under::Test.

Reblessing is a better solution, anyway.

Thanks Ben. An informative post, as usual.

  -Tom

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

Reply via email to