[Perl]use Test::Base;
Test::Base是什么?用官方的说法是“数据驱动的测试”。Test::Base是一个测试框架, 只要给它提供测试数据,它就能自动进行单元测试,省却了手工编写测试程序的麻烦。
可能有人用过Test::More模块进行自动测试,那么我推荐你使用Test::Base。Test::Base 与Test::More完全兼容,也就是说你可以仅仅将use Test::More;换成use Test::Base; 而不用改动任何其他代码;其次,Test::Base可以提供更为简单的测试方法, 让你不必在繁琐的测试程序上花费时间。
我们来看一个具体的例子。下面是我们要测试的程序,函数plural接受一个英文单词, 返回它的复数形式。
package My::Test::English;
# 返回单词的复数
sub plural {
my $word = shift;
# 以s/x/sh/ch结尾的情况
if ( $word =~ /(s|x|sh|ch)$/ ) {
$word .= 'es';
}
# 以辅音+y结尾的情况
elsif ( $word =~ /[^aeiou]y$/ ) {
$word =~ s/y$/ies/;
}
# 以f结尾的情况
elsif ( $word =~ /f$/ ) {
$word =~ s/f$/ves/;
}
# 其他情况
else {
$word .= 's';
}
return $word;
}
1;
让我们来看看如何使用Test::Base对这个函数进行测试。 测试程序如下。文件保存为 plural.t,因为测试用例文件的扩展名通常为 .t。
#!/usr/bin/perl
use My::Test::English;
use Test::Base;
sub plural { My::Test::English::plural(shift) }
run_is 'input' => 'expected';
__END__
=== plural test 1
--- input chomp plural
leaf
--- expected chomp
leaves
=== plural test 2
--- input chomp plural
goose
--- expected chomp
geese
该程序执行后结果如下:
D:\perl>perl plural.t
ok 1 - plural test 1
not ok 2 - plural test 2
# Failed test 'plural test 2'
# in plural.t at line 8.
# got: 'gooses'
# expected: 'geese'
1..2
# Looks like you failed 1 test of 2.
可见,未通过的测试用例会很明显地显示出来,省却了用人眼比较的麻烦。
如果你有多个 .t 文件,可以用 prove 命令,它会依次运行每一个 .t, 并给出全体的测试结果。
D:\perl>prove
.\plural....ok 1/0
.\plural....NOK 2# Failed test 'plural test 2'
# in .\plural.t at line 8.
# got: 'gooses'
# expected: 'geese'
# Looks like you failed 1 test of 2.
.\plural....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 2
Failed 1/2 tests, 50.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
______________________________
.\plural.t 1 256 2 1 50.00% 2
Failed 1/1 test scripts, 0.00% okay. 1/2 subtests failed, 50.00% okay.
让我们来分析一下这段程序。
测试程序开头引用了Test::Base,随后定义了一个名为 plural 的函数, 以调用 My::Test::English::plural。END 标记之后是测试数据, 测试数据的格式如下:
=== 测试数据标题
--- <输入数据名> [<过滤器1> <过滤器2> ...]
输入数据
--- <预期输出数据名> [<过滤器1> <过滤器2> ...]
预期输出数据
测试数据标题可以随便写,仅仅是标识一个数据的开始。数据名就是 run_is 函数中 使用的名称,本例中’input’为输入,’expected’为预期输出。
然后要说明的就是过滤器。Test::Base接收的测试数据都是成对的——一个是输入, 一个是预期的输出,而Test::Base会使用输入数据运行被测试函数, 如果得到的输出结果等于预期输出,则判定为ok,否则为fail。
默认情况下,传递给Test::Base的测试数据都是字符串,显然不能满足各种各样测试的要求, 所以Test::Base提供了filter这个概念,可以通过filter将字符串类型的测试数据转换成所需类型, 再进行测试。再进一步,可以将被测试函数也做成一个filter, 这样输入数据和预期输出数据分别通过各自的过滤器后得到的结果应该相等,
上例中,chomp过滤器表示去掉数据末尾的换行,plural则为自定义的plural函数。 所以上例的过滤器的实际意义就是:
input数据去掉换行后,再用plural函数处理,得到的结果应当与expected数据去掉换行的结果相同。
如果输入数据是数组或散列等复杂数据,则可以使用eval过滤器生成:
=== test
--- input eval testfunc
{ name => 'google', url => 'http://www.google.com' }
--- expected chomp
google
其他的默认过滤器可以参考 perldoc Test::Base::Filter。
这就是Test::Base的实质,通过过滤器来生成数据、执行被测试函数, 最后与预期结果比较。
上面的程序还可以更简单些。首先,用 filters 可以定义输入数据和预期输出数据的默认过滤器, 而不必将过滤器写在每个测试数据上:
filters { 'input' => [ 'chomp', 'plural' ], 'expected' => 'chomp' };
sub plural { My::Test::English::plural(shift) }
__END__
=== plural test 1
--- input
leaf
--- expected
leaves
=== plural test 2
--- input
goose
--- expected
geese
其次,run_is动作可以省略,Test::Base会自动查找测试数据。 (实际上省略run_is后的默认动作为run_compare,与run_is很相似,有兴趣的可以参考 perldoc Test::Base。)
这样最终的测试程序如下:
#!/usr/bin/perl
use My::Test::English;
use Test::Base;
filters { 'input' => [ 'chomp', 'plural' ], 'expected' => 'chomp' };
sub plural { My::Test::English::plural(shift) }
__END__
=== plural test 1
--- input
leaf
--- expected
leaves
=== plural test 2
--- input
goose
--- expected
geese