Skip Menu |

This queue is for tickets about the Test-MockObject CPAN distribution.

Report information
The Basics
Id: 13200
Status: resolved
Worked: 3 hours (180 min)
Priority: 0/
Queue: Test-MockObject

People
Owner: chromatic [...] cpan.org
Requestors: adamk [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.20
Fixed in: (no value)



Subject: Test::MockObject fails for AUTOLOAD call...
I get myself in a corner... a really nasty one. Untestable. Without something like Test::MockObject, yay! I'll try it out for the first time. It will save me. my $Object = Parent->new; my $Mock = Test::MockObject::Extends->new( $Object ); $Mock->mock('mockthis', sub { return 'foo' } ); my $rv = $Mock->foo; is( $rv, "foo", "Mocking worked" ); is( $Parent::somethingnasty, 'Method didn't trigger bad method' ); package Parent; sub new { bless { }, $_[0]; } sub AUTOLOAD { return $_[0]->mockthis(); } our $somethingnasty = ''; sub mockthis { $somethingnasty = 1; } 1; And so with my very first method call via mock, I appear to have hit either a bug or a limitation? You don't check for AUTOLOAD in the things you mock. Is this by intent?
Subject: Re: [cpan #13200] Test::MockObject fails for AUTOLOAD call...
From: chromatic <chromatic [...] wgz.org>
To: bug-Test-MockObject [...] rt.cpan.org
Date: Sun, 12 Jun 2005 21:46:51 -0700
RT-Send-Cc:
On Fri, 2005-06-10 at 20:40 -0400, via RT wrote: Show quoted text
> This message about Test-MockObject was sent to you by ADAMK <ADAMK@cpan.org> via rt.cpan.org > > Full context and any attached attachments can be found at: > <URL: https://rt.cpan.org/Ticket/Display.html?id=13200 > > > I get myself in a corner... a really nasty one. Untestable. > > Without something like Test::MockObject, yay! > > I'll try it out for the first time. It will save me. > > my $Object = Parent->new; > my $Mock = Test::MockObject::Extends->new( $Object ); > $Mock->mock('mockthis', sub { return 'foo' } ); > my $rv = $Mock->foo; > is( $rv, "foo", "Mocking worked" ); > is( $Parent::somethingnasty, 'Method didn't trigger bad method' ); > > package Parent; > > sub new { > bless { }, $_[0]; > } > > sub AUTOLOAD {
warn "<$_[0]>\n"; Show quoted text
> return $_[0]->mockthis(); > } > > our $somethingnasty = ''; > sub mockthis { > $somethingnasty = 1; > } > > 1; > > > And so with my very first method call via mock, I appear to have hit either a bug or a limitation?
It's a limitation. The invocant is always a member of the extended object to all methods of that class. Any method you don't mock on the T::MO::E object passes through to the wrapped object. In my previous design attempts, it was always too fragile to do otherwise, working correctly about half of the time and failing dramatically the rest of the time. Think of it as implementing the Decorator pattern. Show quoted text
> You don't check for AUTOLOAD in the things you mock. Is this by intent?
Yes. If you need AUTOLOAD() to work, either predeclare your subs or override your can() to do the right thing. -- c
[chromatic@wgz.org - Mon Jun 13 00:48:22 2005]: Show quoted text
> On Fri, 2005-06-10 at 20:40 -0400, via RT wrote: >
> > This message about Test-MockObject was sent to you by ADAMK
> <ADAMK@cpan.org> via rt.cpan.org
> > > > Full context and any attached attachments can be found at: > > <URL: https://rt.cpan.org/Ticket/Display.html?id=13200 > > > > > I get myself in a corner... a really nasty one. Untestable. > > > > Without something like Test::MockObject, yay! > > > > I'll try it out for the first time. It will save me. > > > > my $Object = Parent->new; > > my $Mock = Test::MockObject::Extends->new( $Object ); > > $Mock->mock('mockthis', sub { return 'foo' } ); > > my $rv = $Mock->foo; > > is( $rv, "foo", "Mocking worked" ); > > is( $Parent::somethingnasty, 'Method didn't trigger bad method' ); > > > > package Parent; > > > > sub new { > > bless { }, $_[0]; > > } > > > > sub AUTOLOAD {
> > warn "<$_[0]>\n"; >
> > return $_[0]->mockthis(); > > } > > > > our $somethingnasty = ''; > > sub mockthis { > > $somethingnasty = 1; > > } > > > > 1; > > > > > > And so with my very first method call via mock, I appear to have hit
> either a bug or a limitation? > > It's a limitation. The invocant is always a member of the extended > object to all methods of that class. Any method you don't mock on the > T::MO::E object passes through to the wrapped object. In my previous > design attempts, it was always too fragile to do otherwise, working > correctly about half of the time and failing dramatically the rest of > the time. > > Think of it as implementing the Decorator pattern. >
> > You don't check for AUTOLOAD in the things you mock. Is this by
> intent? > > Yes. If you need AUTOLOAD() to work, either predeclare your subs or > override your can() to do the right thing. > > -- c >
Unfortunately, the thing I'm mocking _IS_ the AUTOLOAD. It's for a Template Toolkit hook which translates arbitrary method calls into HTML via an underlying mechanism. There's nothing for it to redirect through to...
Show quoted text
> Any method you don't mock on the T::MO::E object passes > through to the wrapped object.
And that is the point. It doesn't pass through the way it should. If my object has an AUTOLOAD, and I mock ->foo, then a call to ->bar shouldn't just fail silently. It should end up with AUTOLOAD like I would have expected... if (my $mock_method = Test::MockObject->can( $method )) { return $self->$mock_method( @_ ); } + if ($parent->can('AUTOLOAD')) + { + return $self->{_parent}->$method( @_ ); + } Adding that fixes it for me. It makes calls that should go to AUTOLOAD actually go to AUTOLOAD, in cases where the method being called is _not_ mocked. If there are cases where this behaviour makes existing tests fail, let me know and I'll take a look at it. Class::Autouse does a shitload of munging with can/isa/AUTOLOAD, so I can probably fix anything that comes up.
[ADAMK - Sun Jun 26 14:51:48 2005]: Show quoted text
> > Any method you don't mock on the T::MO::E object passes > > through to the wrapped object.
> > And that is the point. It doesn't pass through the way it should.
I've found an approach that appears to do what you want while passing my existing tests. It feels a little bit fragile and I'd like to have more real-world tests in the suite, but version 1.00 should fix this problem.