Perl 构建、单元测试、代码覆盖率:一个完整​​的工作示例

发布于 2024-07-13 15:03:53 字数 356 浏览 8 评论 0 原文

我找到的关于 Perl 构建过程、单元测试和代码覆盖率的大多数 Stackoverflow 答案都只是将我指向 CPAN 那里的文档。 指向 CPAN 模块绝对没有任何问题,因为完整的文档应该位于此处。 不过,在很多情况下,我很难找到完整的工作代码示例。

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

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

Most Stackoverflow answers that I have found in regards to the Perl build process and unit testing and code coverage simply point me to CPAN for the documentation there. There's absolutely nothing wrong with pointing to CPAN modules because that's where the full documentation is supposed to reside. I've had trouble finding complete working code examples in many cases, though.

I've been searching all over the Internet for actual working code samples that I can download or paste into my IDE, like your typical tutorial "Hello World" example source code, but of an example that demonstrates the build process with unit testing and code coverage analysis. Does anyone have a small example of a complete working project that demonstrates these technologies and processes?

(I do have a small working example and I will answer my own question with it, but there are probably other SO users who have better examples than the ones I came up with.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夏天碎花小短裙 2024-07-20 15:03:53

我花了一段时间,也花了我从许多不同的来源获取小片段并将它们融合在一起,但我认为我有一个小的工作示例,可以充分向 Perl 新手演示 Perl 构建过程,包括单元测试和代码覆盖率分析与 报告。 (我在 Windows XP Pro PC 上使用 ActiveState ActivePerl v5.10.0,Module::Build, 测试::更多Devel::Cover)

从 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 protected]>',
    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”文件,在您最喜欢的网络浏览器中打开代码覆盖率报告。 它为您提供了一个漂亮的彩色编码超文本报告,您可以在其中单击文件名并在报告中实际源代码旁边查看 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 设置为通过单击鼠标来执行此文件(通过“perlBuiltTest.PL”),它会自动运行我的单元测试代码IDE 而不是我从命令行手动执行此操作。 将“dispatch('test')”替换为“dispatch('testcover')”以自动执行代码覆盖率。 键入“构建帮助”以获取可从 Module::Build 获得的构建目标的完整列表。

It took me a while and it also took me taking small snippets from a number of different sources and melting them together, but I think I have a small working example that sufficiently demonstrates to a Perl newbie the Perl build process including unit testing and code coverage analysis & reporting. (I'm using ActiveState ActivePerl v5.10.0 on a Windows XP Pro PC, Module::Build, Test::More, Devel::Cover)

Start out with a directory for your Perl project and then create a "lib" directory and a "t" directory under your project directory:

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

In the "lib" directory, create a text file named "HelloPerlBuildWorld.pm". This file is your Perl module that you will be building and testing. Paste the following content into this file:

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;

In the "t" directory, create a text file named "HelloPerlBuildWorld.t". This file is your unit test script that will attempt to fully test your Perl module above. Paste the following content into this file:

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");

Now back up in your top level project directory, create a text file named "Build.PL". This file will create your build scripts that you will use later. Paste the following content into this file:

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 protected]>',
    build_requires => {
        'Test::More' => '0.10',
    },
);

$builder->create_build_script();

That's all the files you need. Now from the command line in the top level project directory, type the following command:

perl Build.PL

You will see something similar to the following:

Checking prerequisites...
Looks good

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

Now you should be able to run your unit tests with the following command:

Build test

And see something similar to this:

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)

To run your unit tests with code coverage analysis, try this:

Build testcover

And you'll see something on the order of this:

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.

(Someone please tell me how to configure Cover to ignore all the Perl libraries except and just report back to me on my single file that I wrote. I could not get Cover filtering to work according to the CPAN documentation!)

Now if you refresh your top level directory, you can see a new subdirectory called "cover_db". Go into that directory and double click on the "coverage.html" file to open the code coverage report in your favorite web browser. It gives you a nice color coded hypertext report where you can click on your file name and see detailed statement, branch, condition, subroutine coverage statistics for your Perl module right there in the report next to the actual source code. You can see in this report that we did not cover the "bye()" routine at all and also there is a line of code that is unreachable that was not covered as we expected.

snapshot of code coverage report
(source: leucht.com)

One more thing you can do to help automate this process in your IDE is to make some more "Build.PL" type files that explicitly perform some of the build targets that we did above manually from the command line. For example, I use a "BuildTest.PL" file with the following content:

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

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

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

Then I set up my IDE to execute this file (via "perl BuiltTest.PL") with a single mouse click and it automatically runs my unit test code from the IDE instead of me doing it manually from the command line. Replace the "dispatch('test')" with "dispatch('testcover')" for automated code coverage execution. Type "Build help" for a complete list of build targets that are available from Module::Build.

小瓶盖 2024-07-20 15:03:53

作为对 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 构建的数据库(因此假设已经运行)。

In response to Kurt, I would propose this alternative to his BuiltTest.PL script.

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

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

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

It reuses the database build by Build.PL (and thus assumes that already ran).

情域 2024-07-20 15:03:53

我在 Intermediate Perl 以及 掌握 Perl。 然而,库尔特给出了很好的总结。

我使用 Module::Release 将所有这些组合到发布脚本中。 我输入一个命令,一切都会发生。

I cover this in Intermediate Perl as well as Mastering Perl. Kurt, however, has given a nice summary.

I combine all of this into a release script using Module::Release though. I type one command and it all happens.

谜兔 2024-07-20 15:03:53

非常有用的 module-starter生成一个易于使用的框架项目,用于处理模块安装、文档创建和模块文件的良好布局,以及 - 我认为 - 代码覆盖率支持。 在我看来,对于任何与 Perl 模块相关的工作来说,这是一个很好的开始。

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

The fantastically helpful module-starter generates an easy-to-use skeleton project which handles module installation, creation of documentation and a good layout for module files to live in, and -- I think -- code coverage support. It's IMO a great start for any Perl module-related endeavour.

Also: using CPAN-related tools like Module::Build -- even for modules which are likely never going to be released publically -- is a very good idea.

隔纱相望 2024-07-20 15:03:53

(披露:我是作者)

按照上述方式对所有内容进行排序后,您可以采取下一步并使用 Devel::CoverX::Covered 例如

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

具体请参阅概要命令行示例。

Devel::PerlySense 中,Emacs 支持显示源代码缓冲区中的覆盖率信息(屏幕截图),并导航至/离开覆盖测试文件。

(disclosure: I'm the author)

Once you have everything sorted as described above, you could take the next step and use Devel::CoverX::Covered to e.g.

  • Given a source file, list the test files that provide coverage to that source file. This can be done on a file, sub routine and row level.
  • Given a test file, list the source files and subs covered by that test file.
  • Given a source file, report efficiently on the coverage details per row, or sub.

See the synopsis for concrete command line examples.

In Devel::PerlySense there's Emacs support to display the coverage information in the source code buffer (screen shot), and to navigate to/from covering test files.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文