Subject: | Test::MockObject::Extends breaks AUTOLOAD |
As I mentioned at OSCON, Test::MockObject::Extends badly breaks with anything that depends on AUTOLOAD.
I've attached a complete test script for this case.
It also threw up another bug as well I'd say is pretty critical, which is that even if a class is already loaded, unless it has it's own file, Test::MockObject::Extends explodes when trying to mock it, because it forces a require. :/
So anyways. Heres a complete test script, go for your life.
#!/usr/bin/perl -w
# Regression Test
#
# The Problem:
# When a class implements most of it's functionality via AUTOLOAD,
# and does not have the option of creating specific methods for
# every possible call (for example, there may be infinite possible
# method names) creating a Test::MockObject::Extends with NOTHING
# mocked should result in a "clear" object.
#
# That is, one that behaves as though it doesn't exist. At time of
# writing, Test::MockObject::Extends would fatally break AUTOLOAD,
# resulting in complete loss of functionality.
#
# This script creates such a "clear" extends object, and tests to
# see that AUTOLOAD is still called correctly.
BEGIN
{
chdir 't' if -d 't';
use lib '../lib';
}
use strict;
use Test::More tests => 11;
use Test::Warn;
my $module = 'Test::MockObject::Extends';
use_ok( $module ) or exit;
#####################################################################
# The Test Class
CLASS: {
package Foo;
use vars qw{$called_foo $called_autoload $method_name};
BEGIN {
$called_foo = 0;
$called_autoload = 0;
$method_name = '';
}
sub new {
bless {}, $_[0];
}
sub foo {
$called_foo++;
return 'foo';
}
sub AUTOLOAD {
$called_autoload++;
$method_name = $Foo::AUTOLOAD;
return 'autoload';
}
1;
}
#####################################################################
# Testing the Class
my $object = Foo->new;
isa_ok( $object, 'Foo' );
# A second bug here.
# Test::MockObject::Extends can't mock a class that doesn't have it's own
# module file. There are other ways to deal with this, see Class::Autouse
# for the way it does it.
my $mock = Test::MockObject::Extends->new( $object );
isa_ok( $mock, $module );
isa_ok( $mock, 'Foo' );
# Call foo
is( scalar($mock->foo), 'foo', '->foo returns as expected' );
is( $Foo::called_foo, 1, '$called_foo is incremented' );
is( $Foo::called_autoload, 0, '$called_autoload is unchanged' );
is( $Foo::method_name, '', '$method_name is unchanged' );
# Call an autoloaded method
is( scalar($mock->bar), 'autoload', '->bad returns as expected' );
is( $Foo::called_autoload, 1, '$called_autoload is unchanged' );
is( $Foo::method_name, 'Foo::bar', '$method_name is unchanged' );