Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

dpisoni (6979)

dpisoni
  (email not shown publicly)
http://www.ourironiclife.com/hyperbole
AOL IM: gefilte3 (Add Buddy, Send Message)

Journal of dpisoni (6979)

Thursday October 26, 2006
06:52 PM

"Scoping token" for class constructor mocking

[ #31429 ]

I have no idea if this is silly, or if everyone-and-their-dog already does this, or if this is novel. Basically, I wanted a way to have a "controlled scope" – I wanted my mock to live only as long as I wanted it to, but I didn't want to have that be within a lexical constraint. (Naturally, if all I cared about were a lexical scope, I would have used local)

Motivation:

  1. Unit Testing
  2. I use Test::Class, which supports the notion of xUnit-style "fixtures", and runs all tests in one interpreter
  3. I am using Test::MockObject to mock objects of my boundary classes, and manually mocking the class constructors
  4. I want the freedom to set up my mocks in a particular "scope" under my complete control. So I want to re-use the actual MockObjects (to better enable changing out the data emitted by the mock methods without having to set them up over and over) but dynamically set up and tear down the constructor mocking.
  5. I use extensively the features of Test::MockObject in my test code. I have no need for any special features of Test::MockModule or Test::MockClass, all I need is to intercept a single constructor method and then restore it back to its original state when I'm done with it.

My solution:

So what I did was create a "scoping token". It's just an empty scalar that has a destructor "attached" to it to handles the cleanup. I suppose this could have been done using overloading (a la Scalar::Defer) but this solution seemed like it would be faster and cleaner.

Here's my method mock_class_constructor(). It takes as its arguments the name of the constructor symbol you are mocking (that is, a fully qualified method name) and the object (assumably a Test::MockObject, but could be anything) that you want it to return.

sub mock_class_constructor {
    my $proto = shift;
    my($constructor_symbol, $return_object) = @_;

    no strict 'refs';
    no warnings 'redefine';
    my $orig_constructor = \&{ "$constructor_symbol" };
    *{ "$constructor_symbol" } = sub { $return_object };

    # orderly cleanup
    my $package = int(rand(time)) . int(rand($$));
    no strict 'refs';
    *{ $package . '::DESTROY' } = sub {
            # Actual cleanup
            no warnings 'redefine';
            *{ "$constructor_symbol" } = $orig_constructor;
            # Cleanup of this closure
            undef *{ $package . '::DESTROY' };
        };

    return bless \do { my $anon_scalar }, $package;
}

So now in my test code, when I want to unit test with the boundary class mocked, I call this first. If I want to switch to a longer integration test, I'll release the token and run this again on a different class (further down the stack.) (Naturally, I don't need this at all for a full-stack integration test.)

Yes, I know I could have had the method accept a closure instead of just an object to return. At that point though I'd just be re-implementing Test::MockModule.

My original design was to have the method return a callback closure that does the cleanup. I dumped this for two reasons:

  • dangerous: no way to ensure that the cleanup would actually be called
  • less useful: couldn't opt to piggyback on an existing scope

With this design, I could do any number of more "natural" scope controls:

  • Confine the token to a lexical scope, cleanup happens when scope terminates. (no real benefit here, could have done this with local())
  • Stash the token in another object's instance data. This essentially binds the scope to that object's, sort of a "piggyback scope". Probably could do this with the MockObject, though I'd have to change the code above to weaken() the $return_object reference otherwise the closure will have a circular reference.
  • Explicity release the token

So I feel like I've got something that might have some useful application, but I also imagine that it's a dead end someone else has already found. I'm interested in any feedback.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • I'd also considered using a similar technique for similar scoping reasons. Make sure you're handling reentrancy correctly (although this may not be a problem if this technique is for unit testing).

    Would it be possible to show the code in which this is used? I'm having trouble thinking of a situation in which local woudn't cut it (although there obviously is if several different people have thought of this technique).

    I really like the name "scripting token". My first thought was to call the scalar a "canary"
    • I can give a little, though I think in most cases I didn't use it in as novel ways as I conceived that I would. (I'll have to redact a bit, but that's okay.) But I have a mean over-engineering streak...

      The method in the journal entry exists in my project Testing superclass (which isa Test::Class.) In addition, there is:

      sub mock_network {
          my $proto = shift;
          $proto->mock_class_constructor( NETWORK_PKG() . '::new', @_ );
      }
      sub mock_implementation {
          my $proto = shi

  • ... not quite the same, but similar motivation. Rather than just "entangling" the scoping token with the thing they want to control scope of however, it wraps it.