I have project where I renamed a Perl module because it had a too generic name and wrote a small compatibility module. I even wrote a test for the compatibility module but it was false positive. Why does this feel so hard to test? How to actually test the existence of the exported function if I can't call it because of side effects?
In my compatibility module I inherited from the new module but didn't import its functions. That wasn't enough, to my surprise. Here I built a minimal example with only 1 package involved:
use strict;
use warnings;
use feature qw(say);
{
package MyPackage;
use parent 'Exporter';
our @EXPORT = qw(foo);
}
MyPackage->import();
say *main::foo{CODE};
say *MyPackage::foo{CODE};
say main->can('foo');
say MyPackage->can('foo');
foo();
(I replaced use MyPackage
with MyPackage->import()
to make this example work in 1 file.)
The output:
CODE(0x55fa5cf9e750)
CODE(0x55fa5cf9e750)
CODE(0x55fa5cf9e750)
CODE(0x55fa5cf9e750)
Undefined subroutine &MyPackage::foo called at inherit.pl line 16.
This all looks like the function exists but it fails when I try to call it.
This is Perl 5.34.
Update to answer zdim’s qestion: What is the actual issue?
I moved the actual code from OldModule.pm
to NewModule.pm
(not their real names, of course) and created a new OldModule.pm
for compatibility with existing software using that. The actual code provided both package functions via import()
as well as class and object methods. So I used use parent 'NewModule'
to inherit all of them. I believed this would also inherit package functions. My Test with Test2’s can_ok
incorrectly showed that it worked.
Thanks to a bug report from a coworker I learned that this was not enough and found I needed to import the package functions as well.
I can’t simply call the functions in question to test the compatibility package because this is part of a CGI app and the functions actually do database operation or print out stuff to STDOUT and would pollute my TAP output. (Yes, part of my mission is to get rid of CGI, of course.)
package OldPackage;
# ABSTRACT: transitional package for new name NewPackage
use strict;
use warnings;
use NewPackage; # this is the line I added to fix the issue
use Exporter qw(import);
our @EXPORT = @NewPackage::EXPORT; # this line only declared undefined functions
1;
foo()
is exported but not defined?" It seems likeExporter.pm
does not care if the sub is defined or not inMyPackage
, it just assigns\&{"MyPackage::foo"}
to*{"main::foo"}
, see line 66 inExporter.pm
– Nephroperl -MCarp::Always
and thought I might see a call to Exporter which then calls MyPackage::foo() but it seems to fail directly. I find this weird. – Heinrick*x{SCALAR}
would given$x = undef;
. – ExordiumExporter
copiesfoo
to the caller's symbol table and that's that; now it's there in the sub's slot and reported as such. Even if it's not defined -- OK, a quirk perhaps. (But one shouldn't do that so it's kinda OK) – Obybless
-es), or import if it isn't; not both. (I'm not saying that doing both causes the problem but it's messy, isn't it.) – ObyExporter
's problem? Or, you can import via@EXPORT_OK
so you have an exact list of names and then write a test for everything that's imported, to defend yourself against that. But if you only inherit then no non-existing "subs" should creep in, right? (All this is shooting in the dark a little since I still don't fully get the problem but perhaps that's just me...) – Oby