in reply to Mocking with namespace::autoclean and Moose::Exporter

You're exporting a function from Logger (into the package MyObj), then compiling a sub that captures a reference to that function, then using namespace::autoclean to remove the function from the package. So, it should be entirely expected that you can't override the function later since you removed it from the MyObj package.

I didn't expect your final solution there to work, but what I think you did was confuse ::autoclean into not removing the function from the package. ::autoclean does heuristic guessing games about where a function came from, so maybe after overriding it with Sub::Override it ends up not getting removed. Personally I prefer namespace::clean since it has a simpler mode of operation.

I think your simplest option is to override the Logger::log_warn at the top of your unit test, globally, and then just work with that.

BEGIN { $override= Sub::Override->new( 'Logger::log_warn' => sub { push @wawrnings, $_[0] if caller eq 'MyObj'; } ) }

Replies are listed 'Best First'.
Re^2: Mocking with namespace::autoclean and Moose::Exporter
by choroba (Cardinal) on Nov 15, 2024 at 19:11 UTC
    Please, try to show more than just a snippet. The exact placement of it might be important. I wasn't able to make it work: without use Logger;, it fails with
    Cannot replace non-existent sub (Logger::log_warn) at t/01-basic.t lin +e 18. BEGIN failed--compilation aborted at t/01-basic.t line 18.

    while with it, it fails a bit later with

    1..4 ok 1 - constructs ok 2 - no warnings ok 3 - constructs Undefined subroutine &MyObj::log_warn called at /home/choroba/_/0/lib/ +MyObj.pm line 11.

    Which is exactly why I asked the original question.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      This is what I had in mind:
      #!/usr/bin/perl use warnings; use strict; use Test::More tests => 2 * 2; use Sub::Override; my @warnings; my $override; use Logger; BEGIN { $override = 'Sub::Override'->new( 'Logger::log_warn' => sub { push @warnings, $_[0] } ); } use MyObj; for my $test ([1, undef, 'no warnings'], [11, 'Value too large', 'warn +ings']) { my ($value, $warnings, $name) = @$test; @warnings = (); my $o = bless {value => $value}, 'MyObj'; ok($o, 'constructs'); $o->foo; is($warnings[0], $warnings, $name); }
      but I hadn't tested it. It does in fact fail, but only because you have a bug that needs fixed in the code being tested :-)

      Edit: Turns out when you replace the Logger sub, Moose::Exporter can't determine the sub name from the coderef. Export it like this and it works:

      my ($import, $unimport, $init_meta) = Moose::Exporter->build_import_me +thods( as_is => ['Logger::log_warn'] ); sub import { goto &$import }