Mocking
作者:gugod 發佈於: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:
- save the original code ref of Player::run
- replace it with the mocked version
- when invoked, simply returned the given return value
- 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
:
- Create a MockedPlayer package with the mocked
run
method defined in it @{MockedPlayer::ISA} = ("Player");
- 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.