在工作单元测试中意识到代码覆盖的悲伤状态后,我正在尝试创建一个实用程序,它将扫描我们的代码库并标记没有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";
}
}
使用CPAN中的Class::Inspector
模块:
for my $classname ( @ARGV ) {
my @methods = Class::Inspector->methods($classname, 'public');
print Dumper \@methods;
}
这两种方法产生类似的结果;这些问题是它们显示整个模块可用的所有方法,而不仅仅是该模块中 中定义的方法。
是否有某种方法可以区分模块可访问的方法和模块内部明确定义的方法?
注意:我没有尝试创建完整的代码覆盖率测试,对于我的用例,我只想测试所有方法至少被调用过一次。像Devel::Cover
这样的全面覆盖测试对我们来说太过分了。
答案 0 :(得分:4)
每个子(或更具体地说,每个CV),记住它最初声明的包。测试用例:
Foo.pm
:
package Foo;
sub import {
*{caller . "::foo"} = sub{};
}
1;
Bar.pm
:
package Bar;
use Foo;
our $bar; # introduces *Bar::bar which does not have a CODE slot
sub baz {}
1;
现在,访问符号表会同时提供foo
和baz
。顺便说一句,我会写这样的代码(原因很快就会明白):
my $classname = 'Bar';
for my $glob (values %{ "${classname}::" }) {
my $sub = *$glob{CODE} or next;
say *$glob{NAME};
}
接下来,我们必须调查B
module以反省underlying C data structure。我们使用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};
}
添加一些健全性检查,这应该可以很好地运作。
不应通过字符串eval
完成动态类/模块加载。相反,我建议Module::Runtime
:
Module::Runtime::require_module($classname);