ron7 has asked for the wisdom of the Perl Monks concerning the following question:

Dear All

I'm writing test cases for something that's already working just fine (yes, I know...)

The main engine module creates one instance of a number of different classes based on user selections. All these do the same thing via a common sub name, returning the same structure, but using vastly different data sources (in another language I'd be talking about polymorphism). Sort of like this:

package BossClass; ... sub new { ... } sub create { my $obj = $a ? Aclass->new(...) : $b ? Bclass->new(...) : Cclass->new(...); my $res = $obj->do_it(...); ... }
The constructor params and method call args can be different in each case and it's those I want to unit test, without actually the class instance in question doing whatever the class's do_it does, if you see what I mean. I've used Test::Resub for this. eg:
use Test::More; use Test::Resub qw/ resub /; my $mocker = resub 'Aclass::do_it', sub { }, capture=>1; my $tobj = BossClass->new(...); $tobj->create(); my $args = $mocker->args; # Tests check the args BossClass provided to the chosen constructor # and the do_it method are as expected
This works just fine for 4 out of 5 of my "polymorphic" classes. On the failing one, I get the following:
The result of B::Deparse::coderef2text was empty - maybe you're trying to serialize an XS function?
I know the constructor was called and I don't believe either the mock or real do_it was called. I'm guessing the error is occurring in the part of Test:Resub magic that compiles a replacement method on the fly, but the "resub" code I'm using for all the polymorphic classes is identical in all cases - just do nothing! Hope someone has encountered this before and can point me in the direction. I've tried temporarily commenting out the entire contents of the offending class's do_it (and all other subs in the class), but no joy: same error message.

Replies are listed 'Best First'.
Re: Perl compiler breaks using Test::Resub
by Corion (Patriarch) on Feb 03, 2011 at 09:56 UTC

    It looks like the offending code in Test::Resub is this:

    ... if ($capture{$ident}) { local $Storable::Deparse = 1; local $Storable::Eval = 1; my $saved_args = $deep_copy{$ident} ? dclone([@_]) : [@_]; push @{$method_args{$ident}}, $saved_args; } ...

    ... so that would seem to me that maybe your actual objects store callbacks as part of their data somewhere, and at least one of these callbacks can't be deparsed by B::Deparse (maybe because it's a function implemented in XS).

    But without seeing more of your code, preferrably a self-contained short program that still exhibits the same problem, it's only guesswork.

    A workaround would be to specify capture => 0, but I'm not sure what implications that has for your test. Likely you won't know with what arguments your subroutine was called. Personally, I don't see what benefit Test::Resub has over simply replacing the function yourself:

    { my @args; local *Aclass::doit = sub { push @args, [@_]; return 42; }; $boss_worker->doit; is 0+@args, 3, "We got called three times"; is_deeply \@args, [[...], [...], [...] ]; }

    Update: Rereading your post topic, are you using the "Perl compiler" perlcc? It has been discontinued in Perl 5.10 (or at least Perl 5.12), as it never really worked. If it fails to work for you, that's unsurprising, and a fix is unlikely to be in sight.

      Thanks all

      I found the problem late last night and looks like Corion had picked the root cause. The constructor of the class giving trouble was creating an object in $self that could not be serialized, hence the problem when Resub tried to capture @_ that would be passed into the real sub.

      As there was no need for the ref to it to be in $self, I removed it and replaced it with a lazy "getter", now all is well.

      Re the perlcc question, my app (27,351 LOC in all modules) uses Tk and I distribute binaries for Win32, Unix, and OSX using ActiveState perlapp to create them (plus the source which I think nobody looks at). Sadly, ActiveState dropped Tk after 5.8, so I HAVE to use 5.8.x. Them's the breaks...

Re: Perl compiler breaks using Test::Resub
by chromatic (Archbishop) on Feb 03, 2011 at 05:28 UTC

    Can you post the offending code itself?

Re: Perl compiler breaks using Test::Resub
by Khen1950fx (Canon) on Feb 03, 2011 at 09:49 UTC
    I used a slimmed-down version of your script, trying to make it as simple as possible. This works for me.
    #!perl use strict; use warnings; use Devel::SimpleTrace; use Test::More tests => 2; use Test::Resub qw(resub); { package BossClass; sub show { my ( $class, $message ) = @_; return "$class, $message"; } } package main; { sub create { my $obj = $_; my $res = $obj->do_it; } my $mocker = resub( 'BossClass::show', sub { 'show' }, capture => +1 ); is( BossClass->show('no_go'), 'show' ); } is( BossClass->show('show'), 'BossClass, show' );