Section 18.4.  Using Mock Objects

Table of Contents

18.4. Using Mock Objects

Sometimes we don't want to ramp up the entire system to test only parts of it. We can be fairly certain, or at least assume, that other parts of the system work. We don't need to open expensive database connections or instantiate objects with large memory footprints to test every part of the code.

The Test::MockObject module creates "pretend" objects. We give it information about the part of the object's interface we want to use, and it pretends to be that part of the interface. Basically, the pretend method has to return the right thing when we call it, and it doesn't have to do any processing.

Instead of creating a real Minnow object, which would mean turning on all sorts of things on the boat, we can create a mock object for it. Once we create the mock object and store it in $Minnow, we tell it how to respond to the methods we need to call. In this case, we tell the mock object to return true for engines_on and to return false for moored_to_dock. We're not really testing the object for the ship, but we want to test our quartermaster object, which takes a ship as an argument. Rather than test the quartermaster with a real ship, we use our mock one.


use Test::More 'no_plan';
use Test::MockObject;

# my $Minnow = Real::Object::Class->new( ... );
my $Minnow = Test::MockObject->new(  );

$Minnow->set_true( 'engines_on' );
$Minnow->set_true( 'has_maps' );
$Minnow->set_false( 'moored_to_dock' );

ok( $Minnow->engines_on, "Engines are on" );
ok( ! $Minnow->moored_to_dock, "Not moored to the dock" );

my $Quartermaster = Island::Plotting->new(
        ship => $Minnow,
        # ...

ok( $Quartermaster->has_maps, "We can find the maps" );

We can create more complex methods that do anything we like. Suppose, instead of methods that return true or false, we need one that returns a list. Perhaps we need to pretend to connect to a database and retrieve some records. As we're developing, we might try this several times and we'd rather not connect and disconnect from the real database every time we try to track down a bug.

In this example, we mock the database method list_names, which we know will return us three names. Since we already know this, and we're actually testing something else (which we don't show you in this contrived example), it doesn't bother us to create the mock method that stands in place of the real database.


use Test::More 'no_plan';
use Test::MockObject;

my $db = Test::MockObject->new(  );

# $db = DBI->connect( ... );
        list_names => sub { qw( Gilligan Skipper Professor ) }

my @names = $db->list_names;

is( scalar @names, 3, 'Got the right number of results' );
is( $names[0], 'Gilligan', 'The first result is Gilligan' );

print "The names are @names\n";

Table of Contents
© 2000- NIV