Mocking

| 1 Comment | No TrackBacks

Rspec provide this super sweet syntax sugar to write mocked methods of any given object:

player.should_receive( :run ).and_return( 42 )

Ref: http://rspec.info/documentation/mocks/message_expectations.html

I’m pondering if this could be ported to Perl.

It’s not so difficult to just override the method. Suppose a Player class with a run method defined like:

package Player;
sub run {
    # calculate total distance of run...
    return $total_distance;
}

The run method can be easily overridden in anywhere by:

*{Player::run} = sub {
    return 42;
}

And it effects on all Player objects, whether they were constructed or not.

Based on this behavior, it might be possible to implement a method-mocking mechanism by:

  1. save the original code ref of Player::run
  2. replace it with the mocked version
  3. when invoked, simply returned the given return value
  4. also, restored the original Player::run method if that’s desired

On the other hand, it can also be done by playing the @ISA a little bit. Suppose our goal is to mock the run method of the Player object $p:

  1. Create a MockedPlayer package with the mocked run method defined in it
  2. @{MockedPlayer::ISA} = ("Player");
  3. re-bless $p as a MockedPlayer

The downside of this approach is that the $p becomes an object of MockedPlayer class. However, the mocked run method is now only installed on the $p object, not all other Player objects as a side effect.

Based on the second approach, it might be possible to have a Mocked class with only AUTOLOAD method in it as a catch-all interface, and dispatch to either the mocked version of subroutine, or the real one, depends on how it’s configured in the test program.

Here’s the code synopsis of that idea:

# Construct a mocked version of Player class with a mocked "run" method
my $p = Mocked("Player", {
    run => sub { 42 }
});

# Invoke the mocked `run` method
$p->run;

# Invoke other non-mocked methods
$p->jump;

Test::MockClass and Test::MockObject has already implemented this idea. Test::MockObject also generates a isa method for the mocked object to report itself as an object of whichever mocked.

However, I still want my test program reads as simple as the rspec statement, it reads better. The most important of all, you don’t really need to know the whole pros and cons and the concept of “Mocking” to do it. You just inject a method with a pre-defined return value such that it reduces the trouble when running tests.

Simple things should be easy.

No TrackBacks

TrackBack URL: http://pub.gugod.org/mt/mt-tb.cgi/226

1 Comment

This is really a great idea. I'm also a big fan of some syntaxic sugar ideas from the Ruby community, and I must say I love to port them to Perl.

If you think a new module should provide that to the CPAN, I can help ;-)

What about something like that:

# enable the AUTOLOAD idea on the Player class
use Test::Mocker, 'Player';

Player->should( run => sub { 42 });

What about that? ;-)

Leave a comment

About this Entry

This page contains a single entry by gugod published on May 28, 2009 8:32 PM.

一切都離不開行銷 was the previous entry in this blog.

Test::Cukes now assumes assertion-based testing code. is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Pages

Powered by Movable Type 4.35-en