获取模块中明确定义的方法/函数列表

Hun*_*len 5 perl code-coverage

在工作单元测试中实现代码覆盖的悲伤状态后,我正在尝试创建一个实用程序,它将扫描我们的代码库并标记没有100%的文件.我找到了两种获得所有方法的方法:

直接访问符号表:

for my $classname ( @ARGV ) {
   eval "require $classname";
   die "Can't load $classname $EVAL_ERROR"
      if $EVAL_ERROR; 

    no strict 'refs';
    METHODS:
    for my $sym ( keys %{ "${classname}::" } ) {
       next METHODS unless defined &{"${classname}::${sym}"};
       print "$sym\n";
   }
}
Run Code Online (Sandbox Code Playgroud)

使用Class::InspectorCPAN中的模块:

for my $classname ( @ARGV ) {
   my @methods = Class::Inspector->methods($classname, 'public');
   print Dumper \@methods;
}
Run Code Online (Sandbox Code Playgroud)

这两种方法产生了类似的结果; 这些问题是它们显示了整个模块可用的所有方法,而不仅仅是该模块内部定义的方法.

有没有办法区分模块可访问的方法和模块内部明确定义的方法?

注意:我没有尝试创建完整的代码覆盖率测试,对于我的用例,我只想测试所有方法至少被调用过一次.完整的覆盖测试Devel::Cover对我们来说太过分了.

amo*_*mon 4

每个子(或更具体地说,每个 CV)都会记住它最初在哪个包中声明。测试用例:

Foo.pm:

package Foo;
sub import {
  *{caller . "::foo"} = sub{};
}
1;
Run Code Online (Sandbox Code Playgroud)

Bar.pm:

package Bar;
use Foo;

our $bar;  # introduces *Bar::bar which does not have a CODE slot
sub baz {}
1;
Run Code Online (Sandbox Code Playgroud)

现在访问符号表会给出foobaz。顺便说一句,我会这样编写代码(原因很快就会清楚):

my $classname = 'Bar';
for my $glob (values %{ "${classname}::" }) {
   my $sub = *$glob{CODE} or next;
   say *$glob{NAME};
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们要深入B模块来反思底层的C数据结构。我们用B::svref_2object函数来做到这一点。这将生成一个B::CV具有方便STASH字段的对象(它返回一个B::HV具有NAME字段的对象):

use B ();
my $classname = 'Bar';
for my $glob (values %{ "${classname}::" }) {
   my $sub = *$glob{CODE} or next;
   my $cv = B::svref_2object($sub);
   $cv->STASH->NAME eq $classname or next;
   say *$glob{NAME};
}
Run Code Online (Sandbox Code Playgroud)

添加一些健全性检查,这应该效果很好。

动态类/模块加载不应通过 string 完成eval。相反,我建议Module::Runtime

Module::Runtime::require_module($classname);
Run Code Online (Sandbox Code Playgroud)