我正在开发一个应用程序,必须逐个替换现有的意大利面条代码。为了实现这一点,我有一个调度程序,在匹配URI时运行所需的HTTP资源,否则使用旧的HTTP资源类。
因此,这个旧的HTTP资源必须require
旧系统的入口点文件,我正在试图弄清楚如何测试这个过程。我现在看到它的方式是我想用mock子例程替换原始require
函数,并检查它是否已使用适当的文件名调用。
这是否可能,如果没有,也许有更好的方法可以做到这一点?
答案 0 :(得分:13)
要在单个包中覆盖require
:
use subs 'require'; # imports `require` so it can be overridden
sub require {print "mock require: @_\n"}
全局覆盖require
:
BEGIN {
*CORE::GLOBAL::require = sub {print "mock require: @_\n"}
}
然后:
require xyz; # mock require: xyz.pm
require Some::Module; # mock require: Some/Module.pm
答案 1 :(得分:10)
全局覆盖require
的更好方法可能是在@INC
中安装一个钩子。这个鲜为人知的功能在the require
documentation的末尾描述。
这是一个简单的示例,它拦截对名称以HTTP开头的模块的任何请求:
BEGIN {
unshift @INC, sub {
my ($self, $file) = @_;
return unless $file =~ /^HTTP/;
print "Creating mock $file\n";
my @code = "1"; # Fake module must return true
return sub { $_ = shift @code; defined $_ };
}
}
require HTTP::Foo;
use HTTPBar;
请注意,这也会模仿use
,因为它基于require
。
答案 2 :(得分:2)
您需要注意的其他一些事项。这可以补充或替换覆盖require
。
您是否知道可以将代码引用添加到@INC
路径中?然后,这些内容将全局应用于use
和require
语句。
引用perldoc require
您还可以通过将Perl代码直接放入@INC数组中,将挂钩插入导入工具。
有三种形式的钩子:子程序引用,数组引用和祝福对象。
子程序引用是最简单的情况。当包含系统遍历@INC并遇到子程序时,将使用两个参数调用此子例程,第一个是对自身的引用,第二个是要包含的文件的名称(例如,“Foo / Bar.pm”) 。子例程应该返回任何内容,或者按以下顺序返回最多三个值的列表:
1。文件句柄,将从中读取文件。
2。对子程序的引用。如果没有文件句柄(前一项),那么这个子程序应该每次调用生成一行源代码,将行写入$ _并返回1,最后在文件末尾返回0.如果有文件句柄,然后调用子程序作为一个简单的源过滤器,该行在$ _中读取。同样,为每个有效行返回1,并在返回所有行后返回0。
3.子程序的可选状态。州以$ _ [1]的形式传递。子例程本身的引用作为$ _ [0]
传递
以下是一个例子:
#!/usr/bin/perl
sub my_inc_hook {
my ($sub_ref, $file) = @_;
unless ($file =~ m{^HTTP/}) {
warn "passing through: $file\n";
return;
}
warn "grokking: $file\n";
return (\*DATA);
}
BEGIN {
unshift(@INC, \&my_inc_hook);
}
use strict;
require warnings;
require HTTP::Bazinga;
HTTP::Bazinga::it_works();
__DATA__
package HTTP::Bazinga;
sub it_works {warn "bazinga!\n"};
1;
产地:
$ perl inc.pl
passing through: strict.pm
passing through: warnings.pm
grokking: HTTP/Bazinga.pm
bazinga!
我相信这适用于perl 5.10.0及更高版本。