我正在开发一套Perl脚本和模块,然后部署在我们公司的不同机器和系统上。 某些设施依赖于可能安装或不安装在不同机器上的特定模块。我使用'eval'来检测这个模块是否可用。
我刚刚收到一个故障报告,结果是用户没有在他的机器上成功安装模块(但没有意识到他没有):但是我的代码中的错误是那个在这种情况下,我没有将错误条件传递到顶层,因此它会丢失,并且脚本只是默默地无法执行其部分功能。
为了调查它,我在我的机器上禁用了特定模块,并轻松找到并修复了问题。但是,除了卸载它之外,我能想到禁用它的唯一方法就是重命名文件(当然,我必须通过sudo来完成)。
我现在正在运行我的所有测试,这个模块不可用,并且它已经抛出了一些我没有正确处理这种情况的地方。
但我现在要做的是为这种情况编写一些测试:但是如何在自动测试中明智地使该模块暂时不可用。我真的不希望我的测试使用sudo来移动模块(我可能在同一时间在机器上做其他事情)。
有没有人知道我可以告诉Perl的方法“无论我在哪里尝试'使用'或'需要'来测试,都找不到这个模块”?
我正在运行Perl 5.10.0(在Fedora 12上),并使用Test :: More和TAP :: Harness。我们的一些安装正在运行Perl 5.8,因此我愿意在测试中使用5.10功能,但不在代码本身中使用。
答案 0 :(得分:11)
有几个CPAN模块正在做这件事。我经常使用的是Test::Without::Module
。另一个是Devel::Hide
。通过@INC
或CORE::GLOBAL::require
加入perl的模块加载,这两个,以及其他一些我现在还不记得的名字,都可以大致相同的方式工作。其详细信息记录在perldoc -f require
。
答案 1 :(得分:4)
正如rafl所说,有一些模块可以做到这一点。
如果你对它的机制感兴趣(除了实现结果),还有两种方法可以做到:
加载后,从命名空间中删除模块。 Test::Without::Module执行此操作 - 详细了解源代码。
防止模块首先被加载。最简单的方法是使用Perl的功能将子例程作为@INC
数组的一部分,该数组在通过use
/ require
加载模块时使用。可以在require's perldoc中找到基于此的理论 - 在文本中搜索单词“hooks”。
子程序引用是最简单的 案件。当包含系统走了 通过@INC并遇到一个 子程序,这个子程序得到 用两个参数调用,第一个 对自己的引用,第二个 要包含的文件的名称 (例如,“Foo / Bar.pm”)。子程序 应该什么也不返回或者 最多三个值列表...
... 2.对子程序的引用。如果没有文件句柄(前一项),那么这个子程序应该为每次调用生成一行源代码,将该行写入$ _并返回1,然后在文件末尾返回0。
所以你要做的是写一个sub(只针对指定的包)返回空代码。
# The following code was not tested - for illustrative purposes only
# MUST be done in the BEGIN block at the very beginning of the test
# BEFORE any "use Module"; lines
push @INC, \&my_sub;
my %prohibited_module_files = map { $_=> 1} ("Foo/Bar.pm", "x.pm");
# Ideally, translate module names into file names
sub empty_module_sub {
$_ = "1;\n"; # Empty module
return 0; # End of file
}
sub my_sub {
my ($coderef, $filename) = @_; # $coderef is \&my_sub
if ($prohibited_modules{$filename}) {
print STDERR "NOT loading module $filename - prohibited!\n";
# Optionally, die here to simulate not finding the module!!!
# Otherwise, load empty package
return (undef, \&empty_module_sub);
}
return undef; # Continue searching @INC for good modules.
}
稍微简单(但不是那么有趣或灵活)的方法将依赖于“require”语义首先检查$INC{$filename}
的事实,如果该键存在于%INC hash中,则认为要加载模块;如果键被映射到一个真值,它认为模块已经被正确加载,而假值则死于“编译失败”类型的错误。因此,您可以在undef
块中的1
下,将相应的键(与模块名称匹配的文件名)下的%INC
或BEGIN
值插入到{{1}}以上在代码的开头。