它是否可测试很大程度上取决于您的环境。您需要问自己以下问题:
- 代码是否依赖生产 Perforce 安装?
- 使用随机值运行代码会干扰生产吗?
- 一遍又一遍地运行具有相同值的代码是否总是产生相同的结果?
- 外部依赖有时会不可用吗?
- 外部依赖是否超出了测试的控制范围?
其中一些因素使得运行测试变得非常困难(但并非不可能)。有些可以通过稍微重构代码来克服。
定义您想要测试的确切内容也很重要。该函数的unit test 将确保它根据您输入的内容返回正确的内容,但您可以控制外部依赖项。另一方面,integration test 将运行外部依赖项。
为此构建集成测试很容易,但我上面提到的所有问题都适用。而且由于您的代码中有exit,因此您无法真正捕获它。您必须将该函数放在脚本中并运行它并检查退出代码,或者使用像 Test::Exit 这样的模块。
您还需要以始终获得相同结果的方式设置 Perforce。这可能意味着那里有您控制的日期和文件。我不知道 Perforce 是如何工作的,所以我不能告诉你如何做到这一点,但总的来说这些东西被称为fixtures。这是您控制的数据。对于数据库,您的测试程序会在运行测试之前安装它们,因此您可以获得可重现的结果。
你也有输出到 STDOUT,所以你也需要一个工具来获取它。 Test::Output 可以做到。
use Test::More;
use Test::Output;
use Test::Exit;
# do something to get your function into the test file...
# possibly install fixtures...
# we will fake the whole function for this demonstration
sub perforce_filelist {
my ($date) = @_;
if ( $date eq 'today' ) {
return qw/foo bar baz/;
}
else {
print "No new files!";
exit 1;
}
}
stdout_is(
sub {
is exit_code( sub { perforce_filelist('yesterday') } ),
1, "exits with 1 when there are no files";
},
"No new files!",
"... and it prints a message to the screen"
);
my @return_values;
stdout_is(
sub {
never_exits_ok(
sub {
@return_values = perforce_filelist('today');
},
"does not exit when there are files"
);
},
q{},
"... and there is no output to the screen"
);
is_deeply( \@return_values, [qw/foo bar baz/],
"... and returns a list of filenames without newlines" );
done_testing;
如您所见,这可以相对轻松地处理该函数所做的所有事情。我们涵盖了所有代码,但我们依赖于外部的东西。所以这不是真正的单元测试。
编写单元测试可以类似地完成。有Test::Mock::Cmd 替换反引号或qx{} 用另一个函数。这也可以在没有该模块的情况下手动完成。如果你想知道如何查看模块的代码。
use Test::More;
use Test::Output;
use Test::Exit;
# from doc, could be just 'return';
our $current_qx = sub { diag( explain( \@_ ) ); return; };
use Test::Mock::Cmd 'qx' => sub { $current_qx->(@_) };
# get the function in, I used yours verbatim ...
my $qx; # this will store the arguments and fake an empty result
stdout_is(
sub {
is(
exit_code(
sub {
local $current_qx = sub { $qx = \@_; return; };
perforce_filelist('yesterday');
}
),
1,
"exits with 1 when there are no files"
);
},
"No new files!",
"... and it prints a message to the screen"
);
is $qx->[0], 'p4 files -e //depot/project/design/...module.sv@yesterday,@now',
"... and calls p4 with the correct arguments";
my @return_values;
stdout_is(
sub {
never_exits_ok(
sub {
# we already tested the args to `` above,
# so no need to capture them now
local $current_qx = sub { return "foo\n", "bar\n", "baz\n"; };
@return_values = perforce_filelist('today');
},
"does not exit when there are files"
);
},
q{},
"... and there is no output to the screen"
);
is_deeply( \@return_values, [qw/foo bar baz/],
"... and returns a list of filenames without newlines" );
done_testing;
我们现在可以直接验证是否调用了正确的命令行,但我们不必费心将 Perforce 设置为实际拥有任何文件,这使测试运行更快并让您独立。您可以在未安装 Perforce 的机器上运行此测试,如果该功能只是整个应用程序的一小部分,并且您在处理不同部分时仍希望运行完整的测试套件,这将非常有用应用程序。
让我们快速看一下第二个示例的输出。
ok 1 - exits with 1 when there are no files
ok 2 - ... and it prints a message to the screen
ok 3 - ... and calls p4 with the correct arguments
ok 4 - does not exit when there are files
ok 5 - ... and there is no output to the screen
ok 6 - ... and returns a list of filenames without newlines
1..6
如您所见,它与第一个示例几乎相同。我也几乎不必更改测试。只是添加了模拟策略。
请务必记住,测试也是代码,同样的质量水平也应该适用于它们。它们充当您的业务逻辑的文档,并为您和您的开发人员(包括未来的您)提供安全网。为此,必须清楚地描述您正在测试的业务案例。
如果您想了解更多关于使用 Perl 进行测试的策略,以及不该做什么,我建议您观看 Testing Lies 的讲座 Curtis Poe。