Perl 脚本中单元测试
原文链接:Perl 脚本中单元测试自动化浅析 Perl 单元测试框架的概述随着敏捷开发模式的流行,如何快速高效地适应不确定或经常性变化的需求显得越来越重要。要做到这一点,需要在开发过程的各个阶段引入足够的测试。而其中单元测试则是保证代码质量的第一个重要关卡。 针对各种不同的语言,都有特有的单元测试框架。比如针对 Java 程序的单元测试框架 JUnit,针对 Python 程序的单元测试框架 PyUnit,针对 XML 程序的单元测试框架 XMLUnit 等。 目前,比较通用的 Perl 单元测试框架模块主要有 Test::Class 和 Test::Unit。 Test::Unit 类似于 JUnit 框架,虽然它提供了通过子类的方式扩展测试类,但由于不基于 Test::Builder,无法用到 Test::Builder 系列测试模块的强大作用。Test::Class 同样支持通过创建子类、孙子类等重用测试类以及管理测试,同时,Test::Class 是基于 Test::Builder 模块创建的,因此可以使用任何 Test::Builder 系列的测试模块,如 Test::More、Test::Exception、Test::Differences、Test::Deep 等。这是 Test::Class 相对于 Test::Unit 的一大优势。 本文将结合具体实例,介绍如何创建基于 Test::Simple、Test::More 和 Test::Class 的 Perl 单元测试框架。 回页首 模块的安装由于 ::Simple、Test::More 和 Test::Class 都不是标准模块,因此需要安装。可以用 CPAN 方式在 root 权限下安装。命令如下: Perl – MCPAN – e ‘ install Test::Class ’ 其他模块的安装方法类似。使用 CPAN 需要连接到网络,如果当前没有网络环境,可以根据事先下载好的模块的 readme 文件中的步骤安装相应模块。 回页首
Perl 单元测试框架本节中,我们假设有一个模块 Hello.pm 需要测试,我们结合不同的 Perl 测试框架,讨论测试代码的写法,并从测试结果介绍他们的特点和作用。 Hello.pm 的源代码如下: 清单 1. 被测对象 Hello.pm 源代码 use strict;
use warnings;
package Hello;
$Hello::VERSION = '0.1';
sub hello {
my ($you)=@_;
return "Hello,$you!";
}
sub bye {
my ($you)=@_;
return "Goodbye,$you!";
}
1;
Test::Simple我们先来介绍最简单、最基础的模块 Test::Simple。之所以说这个模块是最简单最基础的,是因为这个模块只有一个 function ok()。语法如下: Syntax: ok(Arg1,Arg2) Arg1: 布尔表达式,如果这个表达式为真, 这个 testcase passed,否则 failed; Arg2: 这个参数是可选的,用来设置 testcase name。 因此,我们能很轻松地书写基于 Test::Simple 测试框架的测试代码。示例代码 test_simple.perl 如下: 清单 2. Test::Simple 示例代码 use strict;
use warnings;
use Test::Simple tests => 3;
use Hello; # What you're testing.
my $hellostr=Hello::hello('guys');
my $byestr=Hello::bye('guys');
ok($hellostr eq 'Hello,guys!','hello() works');
ok($byestr eq 'Goodbye,'bye() works');
my $helloworld=Hello::hello();
ok($hellostr eq 'Hello,world!','should be hello,world! by default');
需要特别说明的是,在写测试脚本之前,必须事先声明计划执行的 testcase 的个数,如: use Test::Simple tests => 3;
我们在命令行中执行 perl test_simple.perl,观察程序的输出如下: 清单 3. Test::Simple 示例代码执行结果 C:MySpaceworkdir>perl test_simple.perl
1..3
ok 1 - hello() works
ok 2 - bye() works
Use of uninitialized value $you in concatenation (.) or string at Hello.pm line
9.
not ok 3 - should be hello,world! by default
# Failed test 'should be hello,world! by default'
# at hello.t line 12.
# Looks like you failed 1 test of 3.
从测试结果中不难看出,第一个和第二个 testcase 成功通过测试,而第三个 testcase 则失败了。 Test::More 的介绍从字面意思上不难看出 Test::More 比 Test::Simple 提供了更多更广泛的对 testcase 是否成功的支持。下面简单介绍其中的一些常用功能。 和 Test::Simple 一样,Test::More 同样需要事先申明需要测试的 testcase 的个数。比如: use Test::More tests => 10;
然而,你可能在最初并不能预见到底需要测试多少个 testcase。为此 Test::More 提供了另外一种在最下方用 done_testing 的方式来达到这个目的。对应的代码如下: use Test::More;
…… run your tests ……
done_testing($number_of_tests_run);
这时,你甚至可以用 skip_all 来跳过 testcase。 use Test::More skip_all => $skip_reason;
Test::More 中提供了许多使用的方法,表 1 中列举出了其中的一些。 表 1. 常用 Test::More 方法
这里,我们着重介绍其中的几个。
更多方法可以参考 CPAN 上关于 Test::More 的更多的介绍。 基于这些函数,我们能非常方便的设计和实现基于 Test::More 的 testcase。示例代码 test_more.perl 如下: 清单 4. Test::More 示例代码 use strict;
use warnings;
use Test::More tests => 3;
use Hello; # What you're testing.
my $hellostr=Hello::hello('guys');
my $byestr=Hello::bye('guys');
is($hellostr,'Hello,'hello() works');
like($byestr,"/Goodbye/",'bye() works');
cmp_ok($hellostr,'eq','bye() works');
can_ok('Hello',qw(hello bye));
在命令行下运行 perl test_more.perl 后,我们可以看到程序的输出如下: 清单 5. Test::More 示例代码执行结果 C:Perltest> perl more.perl
1..3
ok 1 - hello() works
ok 2 - bye() works
ok 3 - bye() works
ok 4 - Hello->can(...)
# Looks like you planned 3 tests but ran 4.
cmp_ok($hellostr,qw(hello bye));
Test::Class 的介绍定义一个测试类,只需要编写一个从 Test::Class 继承的子类,申明如下: use base qw(Test::Class);
由于 Test::Class 本身没有提供测试函数,而是使用 Test::More 之类的其他测试框架的方法,因此需要申明 Test::More 模块 : use Test::More;
Test::Class 的常用方法包括:
下面的例子是基于 Test::Class 的测试代码 test_class.perl。 清单 6. Test::Class 示例代码 use strict;
use warnings;
use Hello; # What you're testing.
use Test::More;
use base qw(Test::Class);
my $hellostr=Hello::hello('guys');
my $byestr=Hello::bye('guys');
sub initial : Test(setup) {
print "Begin One Test...n";
}
sub end : Test(teardown) {
print "End One Test...n";
}
sub test_hello : Test(1) {
is($hellostr,'hello() works');
}
sub test_bye : Test(1) {
like($byestr,'bye() works');
}
Test::Class->runtests();
在命令行中执行 perl test_class.perl 后的测试结果如下: 清单 7. Test::Class 示例代码执行结果 C:Perltest> perl test_class.perl
Begin One Test...
1..2
ok 1 - bye() works
End One Test...
Begin One Test...
ok 2 - hello() works
End One Test...
回页首
应用实例有了之前对于几个常用 Perl 单元测试框架的介绍,下面我们给出一个具体的实例,来测试一个 CPAN 中的一个 module File::Util。部分单元测试的代码如下。 清单 8. 应用实例代码 #!/usr/bin/perl -w
use strict;
use File::Util;
use Test::More;
use base qw(Test::Class);
my $file;
sub init : Test(startup){
print "####################################################n";
print "This script is used to test some subs in File::Util.n";
$file = File::Util->new();
}
sub shutdown : Test(shutdown){
print "Finished all testcases.n";
print "####################################################n";
}
sub initial : Test(setup) {
print "----------------------------------------------------n";
print "Begin One Test...n";
}
sub end : Test(teardown) {
print "End One Test...n";
print "----------------------------------------------------n";
}
sub test_methods : Test(1) {
can_ok('File::Util',qw(existent line_count list_dir));
}
sub test_existent_true : Test(1) {
open(FILE,">test.txt");
print FILE "This is a test.";
close FILE;
cmp_ok($file -> existent('test.txt'),"==",1,'test_existent_file_exists');
unlink "test.txt";
is($file -> existent('test.txt'),undef,'test_existent_file_not_exists');
}
sub test_line_count : Test(1) {
open(FILE,">test.txt");
print FILE "This is a test.n";
close FILE;
cmp_ok($file -> line_count('test.txt'),'test_line_count = 1');
open(FILE,">test.txt");
print FILE "";
close FILE;
cmp_ok($file -> line_count('test.txt'),'test_line_count = 0');
}
Test::Class->runtests();
程序中设计了三个 testcase,分别用于测试模块中的方法的命名空间可见性、existent 方法和 line_count 方法。测试的结果如下: 清单 9. 应用实例代码执行结果 ####################################################
This script is used to test some subs in File::Util.
----------------------------------------------------
Begin One Test...
1..3
ok 1 - test_existent_file_exists
ok 2 - test_existent_file_not_exists
# expected 1 test(s) in main::test_existent_true,2 completed
End One Test...
----------------------------------------------------
----------------------------------------------------
Begin One Test...
ok 3 - test_line_count = 1
ok 4 - test_line_count = 0
# expected 1 test(s) in main::test_line_count,2 completed
End One Test...
----------------------------------------------------
----------------------------------------------------
Begin One Test...
ok 5 - File::Util->can(...)
End One Test...
----------------------------------------------------
Finished all testcases.
####################################################
# Looks like you planned 3 tests but ran 5.
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |