Perl单元测试 - 子程序是否可测试?

Jon*_*n J 7 tdd perl unit-testing subroutine

我一直在阅读和探索Perl中单元测试和测试驱动开发的概念.我正在研究如何将测试概念融入我的开发中.说我在这里有一个Perl子程序:

sub perforce_filelist {

    my ($date) = @_;

    my $path = "//depot/project/design/...module.sv";
    my $p4cmd = "p4 files -e $path\@$date,\@now";

    my @filelist = `$p4cmd`; 

    if (@filelist) {
        chomp @filelist;
        return @filelist;
    }
    else {
        print "No new files!"
        exit 1;
    }
}
Run Code Online (Sandbox Code Playgroud)

子例程执行Perforce命令并将该命令的输出(文件列表)存储到@filelist数组中.这个子程序可以测试吗?测试返回@filelist的空是否有用?我正在努力教自己如何像单位测试开发人员那样思考.

bri*_*foy 5

有几件事情使得测试perforce_filelist子程序比它需要的更困难:

  • p4路径是硬编码的
  • p4命令在子例程内构造
  • p4命令是固定的(因此,它始终p4是路径中的第一个)
  • 您可以直接从子程序输出
  • 您从子例程内退出

但是,您的子程序的职责是获取文件列表并将其返回.除此之外你做的任何事情都会让你更难测试.如果由于你无法控制它而无法改变它,你可以在将来编写这样的东西:

#!perl -T

# Now perforce_filelist doesn't have responsibility for
# application logic unrelated to the file list 
my @new_files = perforce_filelist( $path, $date );
unless( @new_files ) {
    print "No new files!"; # but also maybe "Illegal command", etc
    exit 1;
    }

# Now it's much simpler to see if it's doing it's job, and
# people can make their own decisions about what to do with
# no new files.
sub perforce_filelist {
    my ($path, $date) = @_;
    my @filelist = get_p4_files( $path, $date ); 
    }

# Inside testing, you can mock this part to simulate
# both returning a list and returning nothing. You 
# get to do this without actually running perforce.
#
# You can also test this part separately from everything
# else (so, not printing or exiting)
sub get_p4_files {
    my ($path, $date) = @_;
    my $command = make_p4_files_command( $path, $date );
    return unless defined $command; # perhaps with some logging
    my @files = `$command`;
    chomp @files;
    return @files;
    }   

# This is where you can scrub input data to untaint values that might
# not be right. You don't want to pass just anything to the shell.
sub make_p4_files_command {
    my ($path, $date) = @_;
    return unless ...; # validate $path and $date, perhaps with logging
    p4() . " files -e $path\@$date,\@now";
    }

# Inside testing, you can set a different command to fake
# output. If you are confident the p4 is working correctly,
# you can assume it is and simulate output with your own
# command. That way you don't hit a production resource.        
sub p4 { $ENV{"PERFORCE_COMMAND"} // "p4" }
Run Code Online (Sandbox Code Playgroud)

但是,您还必须判断这种分解水平是否值得.对于不经常使用的个人工具,可能工作量太大.对于你必须支持并且很多人使用的东西,它可能是值得的.在这种情况下,您可能需要官方的P4Perl API.那些价值判断取决于你.但是,在分解问题之后,做出更大的改变(例如使用P4Perl)不应该像地震一样.


作为旁注而不是我推荐的这个问题,这是&和没有参数列表的用例.在这个"加密上下文"中,子例程的参数列表是@_调用它的子例程.

这些调用继续传递链中的相同参数,这很难输入和维护:

    my @new_files = perforce_filelist( $path, $date );
    my @filelist = get_p4_files( $path, $date ); 
    my $command = make_p4_files_command( $path, $date );
Run Code Online (Sandbox Code Playgroud)

使用&和无参数列表(不是偶数()),它传递@_到下一级别:

    my @new_files = perforce_filelist( $path, $date );

    my @filelist = &get_p4_files; 
    my $command = &make_p4_files_command;
Run Code Online (Sandbox Code Playgroud)