dpisoni's Journal http://use.perl.org/~dpisoni/journal/ dpisoni's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:37:50+00:00 pudge pudge@perl.org Technology hourly 1 1970-01-01T00:00+00:00 dpisoni's Journal http://use.perl.org/images/topics/useperl.gif http://use.perl.org/~dpisoni/journal/ "Scoping token" for class constructor mocking http://use.perl.org/~dpisoni/journal/31429?from=rss <p>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" &#8211;&#160;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 <code>local</code>)</p><p> <strong>Motivation:</strong> </p><ol> <li>Unit Testing</li><li>I use Test::Class, which supports the notion of xUnit-style "fixtures", and runs all tests in one interpreter</li><li>I am using Test::MockObject to mock objects of my boundary classes, and manually mocking the class constructors</li><li>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.</li><li>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.</li> </ol><p> <strong>My solution:</strong> </p><p>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.</p><p>Here's my method <code>mock_class_constructor()</code>. 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.</p><blockquote><div><p> <tt>sub mock_class_constructor {<br>&nbsp; &nbsp; my $proto = shift;<br>&nbsp; &nbsp; my($constructor_symbol, $return_object) = @_;<br> <br>&nbsp; &nbsp; no strict 'refs';<br>&nbsp; &nbsp; no warnings 'redefine';<br>&nbsp; &nbsp; my $orig_constructor = \&amp;{ "$constructor_symbol" };<br>&nbsp; &nbsp; *{ "$constructor_symbol" } = sub { $return_object };<br> <br>&nbsp; &nbsp; # orderly cleanup<br>&nbsp; &nbsp; my $package = int(rand(time)) . int(rand($$));<br>&nbsp; &nbsp; no strict 'refs';<br>&nbsp; &nbsp; *{ $package . '::DESTROY' } = sub {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Actual cleanup<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; no warnings 'redefine';<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; *{ "$constructor_symbol" } = $orig_constructor;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Cleanup of this closure<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; undef *{ $package . '::DESTROY' };<br>&nbsp; &nbsp; &nbsp; &nbsp; };<br> <br>&nbsp; &nbsp; return bless \do { my $anon_scalar }, $package;<br>}</tt></p></div> </blockquote><p>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.)</p><p> <em>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.</em> </p><p>My original design was to have the method return a callback closure that does the cleanup. I dumped this for two reasons:</p><ul> <li> <b>dangerous</b>: no way to ensure that the cleanup would actually be called</li><li> <b>less useful</b>: couldn't opt to piggyback on an existing scope</li></ul><p>With this design, I could do any number of more "natural" scope controls:</p><ul> <li>Confine the token to a lexical scope, cleanup happens when scope terminates. (no real benefit here, could have done this with <code>local()</code>)</li><li>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 <code>weaken()</code> the <code>$return_object</code> reference otherwise the closure will have a circular reference.</li><li>Explicity release the token</li></ul><p>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.</p> dpisoni 2006-10-26T23:52:21+00:00 journal Perl 6 and parrot outsider observations @YAPC::NA http://use.perl.org/~dpisoni/journal/30214?from=rss (This is adapted from something I posted on my <a href="http://www.shopzilla.com/">Shopzilla</a> wiki about YAPC::NA.)<br> <br> At YAPC::NA this year, I heard lots of things about Perl 6 and Parrot, and spoke with many people directly involved with the subject. There seems to be very little consensus about the real state of affairs. These are paraphrased comments from people (individual and composite) who are actually involved in these projects (not just blowhards like myself.) <ul> <li>Parrot VM is broken, and probably won't be fixed any time soon. <ul> <li>Parrot may hold back perl 6</li><li>perl 6 might just take off without Parrot, somehow</li></ul></li> <li>Perl 6 is coming along nicely <ul> <li>Radically better than perl 5</li><li>integrates all the goodness from perl 5, Ruby, etc.</li><li>Perl 6 should render Ruby and JavaScript 2 irrelevant</li></ul></li><li>Perl 6 now! <ul> <li>Of course, <a href="http://www.pugscode.org/">Pugs</a> is running a version of Perl 6 now</li><li>Perl 6 is running in Perl 5 now! (or, the Audrey/Ingy &#246;ne-tw&#246; punch)</li><li> <a href="http://pugs.blogs.com/audrey/">Audrey Tang</a> and others have perl 6 semantics running on perl 5 using source filtering</li><li> <a href="http://blog.ingy.net/">Ingy d&#246;t Net</a> (along with Audrey) has given us <a href="http://search.cpan.org/~ingy/Module-Compile-0.17/lib/Module/Optimize.pm">Module::Compile</a> which will cause perl to do the source-filtering business only once, and "compile" it into post-filtered perl5, which will be saved (cached) and re-run each time (until you want to edit the real source.) Using this in combination with perl 6 source filtering mojo, you can write perl 6 into perl 5 with minimal deployment/execution penalty.</li><li>Other pieces of perl6 have been implemented directly into perl5 (<a href="http://search.cpan.org/search?query=perl6&amp;mode=all">search for perl6 on CPAN</a> to see some of the goodies.)</li></ul></li><li>Perl 6 is vaporware <ul> <li>All of this work has taken too long and hasn't produced anything remotely resembling production code.</li><li>(my blowhard comment) Perl 6 syntax and semantics reinforce complaints perl critics levy against perl (namely, TIMTOWTDI and expressiveness make for <i>less</i> readable language.) See <a href="http://www.ozonehouse.com/mark/blog/code/PeriodicTable.pdf">Periodic Table of Operators</a> for reference.</li><li>Perl 6 won't really be a good convincing case until it's "too late" (which can mean any of a number of possible perl_doomsday scenarios.)</li></ul></li></ul> dpisoni 2006-07-07T19:57:58+00:00 perl6