Perl构建,单元测试,代码覆盖:完整的工作示例


86

我发现的有关Perl构建过程,单元测试和代码覆盖率的大多数Stackoverflow答案只是将我引向CPAN那里的文档。指向CPAN模块绝对没有错,因为应该将完整的文档保存在这里。不过,在很多情况下,我都找不到完整的工作代码示例。

我一直在Internet上搜索可以下载或粘贴到我的IDE中的实际工作代码示例,例如典型的教程“ Hello World”示例源代码,但是该示例演示了使用单元测试和代码进行构建的过程覆盖率分析。有人有一个完整的工作项目的小例子来演示这些技术和过程吗?

(我确实有一个小的工作示例,我将用它回答我自己的问题,但是可能还有其他SO用户拥有比我想出的示例更好的示例。)

Answers:


105

我花了一段时间,还花了许多不同来源的小片段并将它们融合在一起,但是我认为我有一个小的工作示例,足以向Perl新手演示Perl构建过程,包括单元测试和代码覆盖率分析和报告。(我在Windows XP Pro PC上使用ActiveState ActivePerl v5.10.0,模块:: Build测试::更多,开发::封面

从您的Perl项目的目录开始,然后在您的项目目录下创建一个“ lib”目录和一个“ t”目录:

HelloPerlBuildWorld
        |
        |----------> lib
        |
        |----------> t

在“ lib”目录中,创建一个名为“ HelloPerlBuildWorld.pm”的文本文件。该文件是您将要构建和测试的Perl模块。将以下内容粘贴到此文件中:

use strict;
use warnings;
package HelloPerlBuildWorld;

$HelloPerlBuildWorld::VERSION = '0.1';

sub hello {
   return "Hello, Perl Build World!";
}

sub bye {
   return "Goodbye, cruel world!";
}

sub repeat {
   return 1;
}

sub argumentTest {
    my ($booleanArg) = @_;

    if (!defined($booleanArg)) {
        return "null";
    }
    elsif ($booleanArg eq "false") {
        return "false";
    }
    elsif ($booleanArg eq "true") {
        return "true";
    }
    else {
        return "unknown";
    }

   return "Unreachable code: cannot be covered";
}

1;

在“ t”目录中,创建一个名为“ HelloPerlBuildWorld.t”的文本文件。此文件是您的单元测试脚本,它将尝试完全测试上面的Perl模块。将以下内容粘贴到此文件中:

use strict;
use warnings;
use Test::More qw(no_plan);

# Verify module can be included via "use" pragma
BEGIN { use_ok('HelloPerlBuildWorld') };

# Verify module can be included via "require" pragma
require_ok( 'HelloPerlBuildWorld' );

# Test hello() routine using a regular expression
my $helloCall = HelloPerlBuildWorld::hello();
like($helloCall, qr/Hello, .*World/, "hello() RE test");

# Test hello_message() routine using a got/expected routine
is($helloCall, "Hello, Perl Build World!", "hello() IS test");

# Do not test bye() routine

# Test repeat() routine using a got/expected routine
for (my $ctr=1; $ctr<=10; $ctr++) {
    my $repeatCall = HelloPerlBuildWorld::repeat();
    is($repeatCall, 1, "repeat() IS test");
}

# Test argumentTest() 
my $argumentTestCall1 = HelloPerlBuildWorld::argumentTest();
is($argumentTestCall1, "null", "argumentTest() IS null test");

# Test argumentTest("true") 
my $argumentTestCall2 = HelloPerlBuildWorld::argumentTest("true");
is($argumentTestCall2, "true", "argumentTest() IS true test");

# Test argumentTest("false") 
my $argumentTestCall3 = HelloPerlBuildWorld::argumentTest("false");
is($argumentTestCall3, "false", "argumentTest() IS false test");

# Test argumentTest(123) 
my $argumentTestCall4 = HelloPerlBuildWorld::argumentTest(123);
is($argumentTestCall4, "unknown", "argumentTest() IS unknown test");

现在,在顶级项目目录中备份,创建一个名为“ Build.PL”的文本文件。该文件将创建您的构建脚本,供以后使用。将以下内容粘贴到此文件中:

use strict;
use warnings;
use Module::Build;

my $builder = Module::Build->new(
    module_name         => 'HelloPerlBuildWorld',
    license             => 'perl',
    dist_abstract       => 'HelloPerlBuildWorld short description',
    dist_author         => 'Author Name <email_addy@goes.here>',
    build_requires => {
        'Test::More' => '0.10',
    },
);

$builder->create_build_script();

这就是您需要的所有文件。现在,从顶级项目目录中的命令行中,键入以下命令:

perl Build.PL

您将看到类似于以下内容:

Checking prerequisites...
Looks good

Creating new 'Build' script for 'HelloPerlBuildWorld' version '0.1'

现在,您应该可以使用以下命令运行单元测试:

Build test

并看到类似以下内容:

Copying lib\HelloPerlBuildWorld.pm -> blib\lib\HelloPerlBuildWorld.pm
t\HelloPerlBuildWorld....ok
All tests successful.
Files=1, Tests=18,  0 wallclock secs ( 0.00 cusr +  0.00 csys =  0.00 CPU)

要使用代码覆盖率分析运行单元测试,请尝试以下操作:

Build testcover

您会看到一些如下的顺序:

t\HelloPerlBuildWorld....ok
All tests successful.
Files=1, Tests=18, 12 wallclock secs ( 0.00 cusr +  0.00 csys =  0.00 CPU)
cover
Reading database from D:/Documents and Settings/LeuchKW/workspace/HelloPerlBuildWorld/cover_db


----------------------------------- ------ ------ ------ ------ ------ ------
File                                  stmt   bran   cond    sub   time  total
----------------------------------- ------ ------ ------ ------ ------ ------
D:/Perl/lib/ActivePerl/Config.pm       0.0    0.0    0.0    0.0    n/a    0.0
D:/Perl/lib/ActiveState/Path.pm        0.0    0.0    0.0    0.0    n/a    0.0
D:/Perl/lib/AutoLoader.pm              0.0    0.0    0.0    0.0    n/a    0.0
D:/Perl/lib/B.pm                      18.6   16.7   13.3   19.2   96.4   17.6
 ...
[SNIP]
 ...
D:/Perl/lib/re.pm                      0.0    0.0    0.0    0.0    n/a    0.0
D:/Perl/lib/strict.pm                 84.6   50.0   50.0  100.0    0.0   73.1
D:/Perl/lib/vars.pm                   44.4   36.4    0.0  100.0    0.0   36.2
D:/Perl/lib/warnings.pm               15.3   12.1    0.0   11.1    0.0   12.0
D:/Perl/lib/warnings/register.pm       0.0    0.0    n/a    0.0    n/a    0.0
blib/lib/HelloPerlBuildWorld.pm       87.5  100.0    n/a   83.3    0.0   89.3
Total                                  9.9    4.6    2.8   11.3  100.0    7.6
----------------------------------- ------ ------ ------ ------ ------ ------


Writing HTML output to D:/Documents and Settings/LeuchKW/workspace/HelloPerlBuildWorld/cover_db/coverage.html ...
done.

(有人请告诉我如何配置Cover以忽略所有Perl库,而仅在我编写的单个文件上向我报告。根据CPAN文档,我无法使Cover过滤正常工作!)

现在,如果刷新顶级目录,则可以看到一个名为“ cover_db”的新子目录。进入该目录并双击“ coverage.html”文件,以在您喜欢的Web浏览器中打开代码覆盖率报告。它为您提供了一个漂亮的彩色编码超文本报告,您可以在其中单击文件名,并在报告的实际源代码旁边直接查看Perl模块的详细语句,分支,条件,子例程覆盖率统计信息。您可以在此报告中看到,我们根本没有涵盖“ bye()”例程,并且还有一些无法访问的代码没有如我们预期的那样被涵盖。

代码覆盖率报告的快照
(来源:leucht.com

要在IDE中自动执行此过程,您可以做的另一件事是制作更多“ Build.PL”类型的文件,这些文件明确执行我们上面通过命令行手动执行的一些构建目标。例如,我使用具有以下内容的“ BuildTest.PL”文件:

use strict;
use warnings;
use Module::Build;

my $build = Module::Build->resume (
  properties => {
    config_dir => '_build',
  },
);

$build->dispatch('build');
$build->dispatch('test');

然后,我将IDE设置为通过单击鼠标即可执行此文件(通过“ perl BuiltTest.PL”),它会自动从IDE运行我的单元测试代码,而不是从命令行手动执行它。将“ dispatch('test')”替换为“ dispatch('testcover')”,以执行自动代码覆盖。键入“构建帮助”以获取Module :: Build可用的构建目标的完整列表。


1
您建立一个BuiltTest.PL的想法对我来说并不好。您为什么不能只编写一个脚本Build build然后执行Build test呢?
Leon Timmermans

2
Leon,您是否建议使用Perl脚本进行命令行调用?如果是这样,那么我不希望通过命令行方式(如示例BuiltTest.PL文件中那样)以编程方式进行调用的方式来进行命令行调用。
Kurt W. Leucht 09年

1
没必要,请参阅我自己的答案
Leon Timmermans

2
Module :: Build不适用于CPAN。即使CPAN上没有,您仍然可以从各种CPAN工具中获得所有功能。即使它是私有模块,您仍然可以通过相同的过程来构建,测试,分发和安装它。
brian d foy

4
要在Devel :: Cover中过滤结果,请向中添加选项$ENV{HARNESS_PERL_SWITCHES}。例如:-MDevel::Cover=+ignore,.t$,+inc,/app/lib,-select,MyModule.pmwhere/app/lib是应用程序专用库,并且MyModule.pm是受测试的模块。
Michael Carman

14

作为对Kurt的回应,我将提出一种替代他的BuiltTest.PL脚本的方法。

use strict;
use warnings;
use Module::Build;

my $build = Module::Build->resume (
  properties => {
    config_dir => '_build',
  },
);

$build->dispatch('build');
$build->dispatch('test');

它重用Build.PL建立的数据库(因此假定已经运行)。


完善!谢谢莱昂 我知道我的示例出了点问题,但是我自己还是这个Perl构建新手!:-)
Kurt W. Leucht 09年


12

非常有用的帮助module-starter生成了一个易于使用的框架项目,该项目可以处理模块的安装,文档的创建以及模块文件的合理布局,并且-我认为-支持代码覆盖率。对于任何与Perl模块相关的工作,这都是IMO的良好开端。

另外:使用CPAN相关工具Module::Build-即使对于可能永远不会公开发布的模块-也是一个好主意


7

(披露:我是作者)

按照上面的描述对所有内容进行排序后,就可以进行下一步,并使用Devel :: CoverX :: Covered到例如

  • 给定一个源文件,列出列出该源文件的测试文件。这可以在文件,子例程和行级别上完成。
  • 给定一个测试文件,列出该测试文件涵盖的源文件和子目录。
  • 给定一个源文件,可以有效地报告每行或每个子项目的覆盖范围详细信息。

概要具体的命令行示例,。

Devel :: PerlySense中,Emacs支持在源代码缓冲区中显示覆盖率信息(屏幕截图),并导航到覆盖测试文件或从覆盖测试文件导航。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.