如何防止Perl使用模块进行测试?

如何防止Perl使用模块进行测试?,perl,testing,Perl,Testing,我正在开发一套Perl脚本和模块,然后部署到我们公司的不同机器和系统上。 某些设备依赖于特定模块,该模块可能安装在不同的机器上,也可能不安装在不同的机器上。我已使用“eval”检测此模块是否可用 我刚刚收到一份故障报告,归结为用户没有成功地在他的机器上安装模块(但没有意识到他没有):但我代码中的错误是,在这种情况下,我没有将错误条件传递到顶层,所以它丢失了,而脚本只是默默地无法执行其部分功能 为了进行调查,我禁用了机器上的特定模块,并很容易地发现并修复了问题。但除了卸载它,我能想到的唯一禁用它的

我正在开发一套Perl脚本和模块,然后部署到我们公司的不同机器和系统上。 某些设备依赖于特定模块,该模块可能安装在不同的机器上,也可能不安装在不同的机器上。我已使用“eval”检测此模块是否可用

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

为了进行调查,我禁用了机器上的特定模块,并很容易地发现并修复了问题。但除了卸载它,我能想到的唯一禁用它的方法是重命名文件(当然,这是我必须通过sudo完成的)

我现在正在运行我所有的测试,但这个模块不可用,并且它引发了一些其他地方,我没有正确处理这种情况

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

有人知道我可以告诉Perl的一种方法吗“无论我在哪里尝试‘使用’或‘要求’它,为了测试目的,都找不到这个模块”


我正在运行Perl5.10.0(在Fedora12上),并使用Test::More和TAP::Harness。我们的一些安装正在运行Perl5.8,因此我愿意在测试中使用5.10特性,但不在代码本身中使用

有几个CPAN模块正在这样做。我经常使用的是。另一个是。这两个人,以及其他几个我现在记不清名字的人,都以几乎相同的方式工作,通过
@INC
CORE::GLOBAL::require
连接到perl的模块加载。详细信息记录在perldoc-f require中,正如rafl所说,有一些模块可以做到这一点

如果你对它的机制也感兴趣(除了获得结果之外),有两种方法可以做到这一点:

  • 加载模块后,将其从命名空间中删除。做这个-查看源代码了解详细信息

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

  • 子程序引用是最简单的 案例当包容系统运行时 通过@INC并遇到 子程序,这个子程序得到 使用两个参数调用,第一个 一个对自身的引用,第二个 要包含的文件的名称 (例如,“Foo/Bar.pm”)。子程序 应返回nothing或a 最多三个值的列表

    。。。 2.对子程序的引用。如果没有filehandle(上一项),则该子例程将在每次调用中生成一行源代码,将该行写入$并返回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散列中,则认为要加载模块;如果键映射到真值,则认为模块已正确加载,而假值则会导致“编译失败”类型的错误。因此,您可以实现与上面的自定义子项类似的结果,在适当的键(文件名与模块名匹配)下插入
    unde
    1
    值在代码开头的
    BEGIN
    块中插入
    %INC

    注意
    Test::Without::Module
    也需要挂钩。它只是碰巧有一些额外的魔力,使已经加载的模块看起来好像从未加载过一样,这取决于您对它们的观察程度。@rafl-好的,需要重新读取POD/源。。。没注意到,谢谢!另外,它是否会“要求”死亡?注意:上面的示例代码将在它的目的中失败,如果您的代码在执行这个示例之后执行任何聪明的<代码> @ INC > /代码>操作,它将预处理到<代码> @ INC >代码(在您刚才插入的自定义子例程之前)。NOT2:还需要考虑是否要传播到子进程。(通过设置
    $ENV{PERL5OPT}
    )-像往常一样,真正可靠的代码的魔鬼在于细节,这就是为什么,撇开自我教育不谈,CPAN模块通常应该用于实际用途的原因-Devel::Hide这样做,例如:)它不会死在
    要求的
    中,如果这是你要问的,但它确实会导致
    require
    通过向其提供带有错误返回值的内容来引发异常本身。通常,您可以通过安装并使用自己的PERL进行测试来绕过sudo问题。我建议你假装系统管理员不在那里。