代码之家  ›  专栏  ›  技术社区  ›  Kurt W. Leucht

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

  •  83
  • Kurt W. Leucht  · 技术社区  · 16 年前

    我在Perl构建过程、单元测试和代码覆盖率方面找到的大多数Stackoverflow答案只是简单地将我指向CPAN以获取那里的文档。指向CPAN模块绝对没有错,因为完整的文档应该驻留在那里。不过,在许多情况下,我很难找到完整的工作代码示例。

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

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

    5 回复  |  直到 15 年前
        1
  •  108
  •   Glorfindel Doug L.    6 年前

    这花了我一段时间,也花了我从许多不同的源代码中提取一些小片段并将它们融合在一起,但我认为我有一个小的工作示例,足以向Perl新手演示Perl构建过程,包括单元测试和代码覆盖率分析&报告。(我正在使用 ActiveState ActivePerl Windows XP Pro PC上的v5.10.0, Module::Build , Test::More , 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_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()”例程,还有一行代码是无法访问的,但没有像我们预期的那样介绍。

    snapshot of code coverage report
    (来源: 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获得的构建目标的完整列表。

        2
  •  14
  •   Leon Timmermans    16 年前

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

        3
  •  12
  •   brian d foy    16 年前

    我把这件事写在 Intermediate Perl 以及 Mastering Perl . 然而,库尔特给出了一个很好的总结。

    我使用 Module::Release 虽然我键入一个命令,一切都发生了。

        4
  •  12
  •   Gaurav    16 年前

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

    另外:使用CPAN相关工具,如 Module::Build --即使是那些可能永远不会公开发布的模块-- is a very good idea .

        5
  •  7
  •   jplindstrom    15 年前

    (披露:我是作者)

    一旦您按照上述方式对所有内容进行了排序,您就可以进行下一步并使用 Devel::CoverX::Covered 到,例如。

    • 给定一个测试文件,列出该测试文件包含的源文件和子文件。
    • 给定源文件,高效地报告每行或子行的覆盖率详细信息。

    synopsis 具体的命令行示例。

    在里面 Devel::PerlySense Emacs支持在源代码缓冲区中显示覆盖率信息( screen shot ),并在覆盖测试文件之间导航。