Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Micro Mocking: using local to help test subs

by adrianh (Chancellor)
on Jan 13, 2003 at 01:19 UTC ( #226368=perlmeditation: print w/ replies, xml ) Need Help??

Writing this node reminded me of a testing technique I occasionally find useful.

Consider the following code:

package DeepThought; sub new { bless {}, shift }; sub foo { my $self = shift; $self->bar(42); }; sub bar { my ($self, $n) = @_; print "$self says the answer is $n\n"; };

I want to test that foo calls bar. I could check the text output by bar, but that ties my test to the what bar outputs. Since this can change independently I want to avoid this.

Solution: use local to redefine bar for the scope of the test. For example:

use Test::More tests => 2; isa_ok(my $o = DeepThought->new, 'DeepThought'); { my $ok; no warnings; local *DeepThought::bar = sub { $ok = 1 if $_[1] == 42 }; use warnings; $o->foo(); ok($ok, 'foo called bar'); };

This technique allows you to mock only part of the class you are testing (hence "Micro Mocking" :-). Occasionally very useful.

You can, of course, use the same method for other classes too. For example, if you had:

package Foo; use CGI; sub new { my $class = shift; bless {cgi => CGI->new}, $class; };

You could check that Foo->new called CGI->new like this:

use Test::More tests => 1; { my $called = 0; no warnings; local *CGI::new = sub { $called = 1 }; use warnings; my $o = Foo->new; ok($called, 'Foo->new called CGI->new'); };

But, by this point, Test::MockObject is probably a more sensible choice :-)

Comment on Micro Mocking: using local to help test subs
Select or Download Code
Replies are listed 'Best First'.
Re: Micro Mocking: using local to help test subs
by Zaxo (Archbishop) on Jan 13, 2003 at 01:47 UTC

    I have used a similar technique to subvert Perl builtins and mimic system errors:

    use Errno qw( ENOSPC ); { local *CORE::GLOBAL::print = sub { $! = ENOSPC; return}; # make test call # see what happens when a device is full }
    A finer grained and more accurate test can be obtained by localizing an open file handle:
    { open local(LOG), '>>', '/dev/full' or die $!; # /dev/full is a Linux thing # call the test }
    ++adrianh for bringing this up, there is a wealth of devious fun in this idiom.

    After Compline,
    Zaxo

Re: Micro Mocking: using local to help test subs
by chromatic (Archbishop) on Jan 13, 2003 at 17:25 UTC

    I've been meaning to write Test::MockPackage (or Test::MockModule) for quite some time now. It'd be handy to gather the most common idioms:

    • always return this value
    • return a series of values on subsequent calls
    • always return true/false
    • execute this sub
    • log the call

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://226368]
Approved by rob_au
Front-paged by sauoq
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (18)
As of 2015-07-31 17:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (279 votes), past polls