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