So, I had a user of my Mock::Sub distribution file an issue report where trying to mock a subroutine that has prototypes threw a warning. After monkeying about with it, I was able to quell said warnings, but it raised something else that I don't understand. I'll post some code and context, then get to what I'm hoping to have answered.
This is a runnable example based on the snippet that the user reported:
use warnings; use strict; use Mock::Sub; my $m = Mock::Sub->new; sub foo ($$$){ return undef; } my $foo_sub = $m->mock('foo');
Result:
Prototype mismatch: sub main::foo ($$$) vs none at /home/stevieb/perl5 +/perlbrew/perls/perl-5.26.1/lib/site_perl/5.26.1/Mock/Sub/Child.pm li +ne 122. Prototype mismatch: sub main::foo: none vs ($$$) at /home/stevieb/perl +5/perlbrew/perls/perl-5.26.1/lib/site_perl/5.26.1/Mock/Sub/Child.pm l +ine 140.
All well and good. As a response to the user, I drummed up a test script with a signal handler to catch Prototype warnings, and just evaporate them (I'll incorporate this into the distribution directly if the reporter is satisfied, enabled only if requested explicitly):
use warnings; use strict; use feature 'say'; use Mock::Sub; $SIG{__WARN__} = sub {say $_[0] if $_[0] !~ /Prototype/}; my $m = Mock::Sub->new; sub foo ($$$){ say "hello!"; # doesn't get called return undef; } my $foo = $m->mock('foo'); $foo->return_value('test'); say foo(1, 2, 3); say foo(1, 2, 3); say foo(1, 2, 3); say $foo->called_count;
That code does the right thing, insofar that the mocked sub is properly called and the prototype warnings are no longer displayed:
test test test 3
Now, what I found while testing, is that initially, I called foo() with no parameters, but the prototype stuck, resulting in fatality. Instead of foo(1, 2, 3); (proper number of params), I had just foo(), and...
Not enough arguments for main::foo at mock.pl line 21, near "()" Execution of mock.pl aborted due to compilation errors.
All I do when I mock out a sub, is overwrite the symbol table for it (actual code from the module):
{ no strict 'refs'; no warnings 'redefine'; my $mock = $self; weaken $mock; *$sub = sub { @{ $mock->{called_with} } = @_; ++$mock->{called_count}; if ($mock->{side_effect}) { if (wantarray){ my @effect = $mock->{side_effect}->(@_); return @effect if @effect; } else { my $effect = $mock->{side_effect}->(@_); return $effect if defined $effect; } } return if ! defined $mock->{return}; if ($mock->{return}[0] && $mock->{return}[0] eq 'params'){ return ! wantarray ? $_[0] : @_; } else { return ! wantarray && @{ $mock->{return} } == 1 ? $mock->{return}[0] : @{ $mock->{return} }; } }; }
My question here, is if the symtab entry was overwritten correctly (ie. the mocked sub is most definitely called as desired), why does perl still think that it requires the prototyped parameters? Clearly, that information is stored somewhere, but where and how?
Can someone point me in a direction I can look down to understand this, or even explain it to me?
Thanks, as always,
-stevieb
In reply to Prototypes required even after mocking a sub by stevieb
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |