我想改变sub redefine的行为

时间:2014-03-21 15:07:54

标签: perl function module

所以我在设计中得到错误。子...重新定义。

假设我有一个子Get_Name_SSN ...进行数据库调用等。 我并不总是想在测试时调用db。 所以我创建了Testing.pm,它硬编码了一些在测试过程中使用的值。 我放use Testing;现在我不需要调用db,如果我注释掉我自己的生产子程序。 我宁愿不通过我的代码评论出众多功能。我怀疑是否可能,但是我可以告诉perl使用导入的模块库中的函数而不是当前脚本中的函数吗?

MyScript.pl

use strict;
use warnings;
#only linux
#use diagnostics;

use TestModule;

sub get_name_ssn {
    #call db
}

##################
package TestModule;
use Exporter;

@ISA = qw(Exporter);
@EXPORT = qw(get_name_ssn ...

sub get_name_ssn {
    #return key value pair
}

因此,当我想要制作时,注释掉使用TestModule。当我测试时,请离开TestModule。这不起作用,除非我也注释掉了本地子。

3 个答案:

答案 0 :(得分:0)

将<{1}}行放在子例程定义之后。目前,use TestModule中的子例程是最后定义的子例程,因此这是分配给名称MyScript.pl的子例程。如果先放置子例程定义然后导入get_ssn_name,则TestModule中的子例程将是最后定义的子例程。

示例:

TestModule.pm

# TestModule.pm sub main::foo { print "test function" }; 1; # script1.pl use TestModule; sub foo { print "real function" }; foo(); # script2.pl sub foo { print "real function" }; use TestModule; foo(); 输出:

script1.pl

real function 输出:

script2.pl

答案 1 :(得分:0)

最后一个定义获胜,因此请确保您想要的那个定义最后显示。

您可以在运行时使用require加载模块。到那时,即使require行在词法上排在第一位,脚本中的那个也已定义。您可以将use更改为:

require TestModule;
TestModule->import;

然而,这个问题有一个更好的解决方案。看起来您想要提供一个虚假的数据库例程进行测试。使用某种形式的依赖注入,而不是杂技,因此您可以自己提供数据库句柄。然后,您可以提供模拟数据库句柄进行测试。

答案 2 :(得分:0)

与brian_d_foy的建议一样,听起来你需要一个测试数据库。如果您有一个集中模块来保存数据库句柄,那么您将很容易触发整个代码库连接到该数据库而不是主数据库。然后你可以做任何正常的程序并测试你的真实代码。

以下是该类型数据库模块的示例。我已将其命名为MyProject::Database。如果使用use MyProject::Database qw($dbh);调用它将返回一个实时数据库句柄。但是,如果使用use MyProject::Database qw($dbh --testing);进行调用,它将连接到测试数据库,而对此$dbh的所有正常调用也必须使用测试数据库:

package MyProject::Database;

### This module is used to create and cache a Database Handle.

use base qw(Exporter);
our @EXPORT = qw($dbh);
our @EXPORT_OK = qw(getConnection)
;
use DBI;

our $DB_NAME = 'MyDB';
our $DB_USER = 'user';
our $DB_PASS = 'pass';
our $DB_HOST = 'localhost';

our $DB_TEST = 'MyTestingDB';

use strict;
use warnings;

# Global Database Handle
#    One unique global database handle is sufficient for most environments.
# For that reason we provide this handle as a shared resource to all packges
# that use this module.
our $dbh;


# Initialize Database Handle
#    This initializes the database handle when this module is used for the
# first time.  For Apache, this should happen each time a child process is
# spawned.
sub import {
    my $module = shift;

    # Filter out options that cause alternative behavior
    my %options;
    @_ = map {/^-/ ? $options{$_}++ && () : $_} @_;
    my $is_testing = delete $options{--testing};
    die "Unknown options passed to " . __PACKAGE__ . ": " join(', ', keys %options)
        if keys %options;

    if ($is_testing) {
        if ($dbh) {
            warn "DBH already created in testing mode.  Will be clobbered.  Be sure to make call to for testing mode before all other use statements";
        }
        $dbh = getConnection($DB_TEST);
    }

    $dbh ||= getConnection();

    MyProject::Database->export_to_level(1, $module, @_);
}


# Child Initialization Handler
#    In some versions of apache/perl, the child initialization is not done
# properly, and the database handle is therefore no initialized.  If this
# occurs, then a ChildInitHandler can be added pointing to here to ensure
# that a connect gets established.
sub handler {
    $dbh = getConnection();
}


# Create a Database Handle
#    This function creates a new Database Handle.  It can be called with alternate
# connect parameters for testing environments etc.
sub getConnection {
    my $name = shift // $DB_NAME;
    my $user = shift // $DB_USER;
    my $pass = shift // $DB_PASS;
    my $host = shift // $DB_HOST;

    # Database URL
    my $url = "dbi:mysql:$name:$host";

    my $handle = DBI->connect($url, $user, $pass) or die "DB connect failed: $DBI::errstr";

    return $handle;
}

1;

__END__

然后将您的脚本编辑为以下内容:

#MyScript.pl

use strict;
use warnings;

use MyProject::Database qw($dbh --testing);

#...

my $database = $dbh->do(q{SELECT DATABASE()}) or die $dbh->errstr;
die "Not Testing DB" if $database ne 'MyTestingDB';

如果您还担心测试脚本编写者不记得指示测试模式,那么您可以强制所有测试脚本以.t结尾。然后让MyProject::Database::import验证$0不会在.t中结束,除非它在测试模式中被调用。