为了测试目的,我如何防止Perl使用模块?

时间:2010-10-11 15:08:35

标签: perl testing

我正在开发一套Perl脚本和模块,然后部署在我们公司的不同机器和系统上。 某些设施依赖于可能安装或不安装在不同机器上的特定模块。我使用'eval'来检测这个模块是否可用。

我刚刚收到一个故障报告,结果是用户没有在他的机器上成功安装模块(但没有意识到他没有):但是我的代码中的错误是那个在这种情况下,我没有将错误条件传递到顶层,因此它会丢失,并且脚本只是默默地无法执行其部分功能。

为了调查它,我在我的机器上禁用了特定模块,并轻松找到并修复了问题。但是,除了卸载它之外,我能想到禁用它的唯一方法就是重命名文件(当然,我必须通过sudo来完成)。

我现在正在运行我的所有测试,这个模块不可用,并且它已经抛出了一些我没有正确处理这种情况的地方。

但我现在要做的是为这种情况编写一些测试:但是如何在自动测试中明智地使该模块暂时不可用。我真的不希望我的测试使用sudo来移动模块(我可能在同一时间在机器上做其他事情)。

有没有人知道我可以告诉Perl的方法“无论我在哪里尝试'使用'或'需要'来测试,都找不到这个模块”?

我正在运行Perl 5.10.0(在Fedora 12上),并使用Test :: More和TAP :: Harness。我们的一些安装正在运行Perl 5.8,因此我愿意在测试中使用5.10功能,但不在代码本身中使用。

2 个答案:

答案 0 :(得分:11)

有几个CPAN模块正在做这件事。我经常使用的是Test::Without::Module。另一个是Devel::Hide。通过@INCCORE::GLOBAL::require加入perl的模块加载,这两个,以及其他一些我现在还不记得的名字,都可以大致相同的方式工作。其详细信息记录在perldoc -f require

答案 1 :(得分:4)

正如rafl所说,有一些模块可以做到这一点。

如果你对它的机制感兴趣(除了实现结果),还有两种方法可以做到:

  1. 加载后,从命名空间中删除模块。 Test::Without::Module执行此操作 - 详细了解源代码。

  2. 防止模块首先被加载。最简单的方法是使用Perl的功能将子例程作为@INC数组的一部分,该数组在通过use / require加载模块时使用。可以在require's perldoc中找到基于此的理论 - 在文本中搜索单词“hooks”。

  3.   

    子程序引用是最简单的   案件。当包含系统走了   通过@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下,将相应的键(与模块名称匹配的文件名)下的%INCBEGIN值插入到{{1}}以上在代码的开头。