【问题标题】:What is the preferred unit testing framework for Perl?Perl 首选的单元测试框架是什么?
【发布时间】:2021-06-16 04:44:10
【问题描述】:

我对 Perl 有点陌生,我想知道是否有首选的单元测试框架?

Google 向我展示了一些不错的结果,但由于我是新手,我不知道社区内是否有明确的偏好。

【问题讨论】:

    标签: perl unit-testing


    【解决方案1】:

    Perl 附带了大量出色的测试工具! Perl 核心有数以万计的自动检查,并且大部分都使用这些标准 Perl 框架。它们都使用 TAP 绑定在一起 - Test Anything Protocol

    在 Perl 中创建 TAP 测试的标准方法是使用 Test::More 系列软件包,包括 Test::Simple 用于入门。这是一个简单的例子:

    use 5.012;
    use warnings;
    
    use Test::More tests => 3;
    
    my $foo = 5;
    my $bar = 6;
    
    ok $foo == 5, 'Foo was assigned 5.';
    ok $bar == 6, 'Bar was assigned 6.';
    ok $foo + $bar == 11, 'Addition works correctly.';
    

    输出将是:

    ok 1 - Foo was assigned 5.
    ok 2 - Bar was assigned 6.
    ok 3 - Addition works correctly.
    

    基本上,要开始,您需要做的就是传递一个布尔值和一个解释应该发生什么的字符串!

    一旦您完成了这一步,Test::More 就会有大量其他功能可以让您更轻松地测试其他内容(字符串、正则表达式比较、深层结构比较),并且有 Test::Harness 后端可以让您进行测试大量单独的测试脚本组合在一起。

    最重要的是,正如Schwern 所指出的,几乎所有现代Test:: 模块都可以协同工作。这意味着您可以将Test::Class(正如Markus 指出的那样)与rjhanswer 中列出的所有出色模块一起使用。事实上,因为Test::Builder--Test::More 和其他人所构建的工具(目前由 Schwern 维护......感谢 Schwern!)--如果需要,您可以从头开始构建您的 OWN 测试子例程这将适用于所有其他测试框架。在我看来,仅此一点就使 Perl 的 TAP 系统成为最好的系统之一:一切都可以协同工作,每个人都使用相同的工具,而且您只需很少的额外工作即可添加到框架以满足您的需求。

    【讨论】:

    • 您不必这样做。但是,如果您的测试脚本在仅输出 1 或 2 个测试的结果后就死掉了,Test::More 就会知道您的测试已经失败(即使它以零错误代码退出)。您也可以在脚本末尾执行use Test::More 'no_plan' 或调用done_testing()
    • 您使用的是什么版本的 Perl 和 Test::More? perl -v 将为您提供 perl 版本,您可以从 perl -MTest::More -e "print Test::More->VERSION()" 获得 Test::more 无论哪种方式,如果可以的话,最好有一个计划。一开始我发现它很乏味,但后来当测试意外中途死亡并且没有报告我的几个测试用例时,我发现了它的救命稻草。如果不是计划,我永远不会知道有问题。
    • 在较新的 Test::More (>=0.88) 上,use Test::More; 最后是 done_testing; 应该可以正常工作。否则,你需要做use Test::More qw(no_plan)
    • @Mike:这只是另一个可用的故障保护;这不是必需的。理想情况下,您已经知道运行了多少测试。事实上,Test::More 会告诉你。例如,如果您在脚本中添加另一个测试而不更改顶部的 tests 声明,它将显示类似 # Looks like you planned 3 tests but ran 4. 的内容,因此更改它很简单。我还认为,单元测试的全部意义在于“硬编码”要尝试的值。如果您的单元测试每次都运行可变数量的步骤,那么现在它的可重复性不是很高,是吗? :)
    • 值得注意的是,几乎所有现代Test:: 模块都可以协同工作。 Test::More可以与Test::DifferencesTest::Deep等组合。
    【解决方案2】:

    Perl 最流行的测试“框架”是一种称为 TAP(测试任何协议)的测试结果格式,它是一组字符串,如下所示:

    ok 1 - Imported correctly
    ok 2 - foo() takes two arguments
    not ok 3 - foo() throws an error if passed no arguments
    

    任何可以生成这些字符串的脚本都算作 Perl 测试。您可以使用Test::More 为各种条件生成 TAP - 检查变量是否等于值,检查模块是否正确导入,或者两个结构(数组/哈希)是否相同。但在真正的 Perl 精神中,有不止一种方法可以做到这一点,还有其他方法(例如 Test::Class,它看起来有点像 JUnit!)

    一个简单的测试脚本示例(它们通常以.t 结尾,例如foo.t

    use strict;
    use warnings;
    use Test::More tests => 3;  # Tell Test::More you intend to do 3 tests
    
    my $foo = 3;
    ok(defined $foo, 'foo is defined');
    is($foo, 3, 'foo is 3');
    $foo++;
    is($foo, 4, 'incremented foo');
    

    您可以使用Test::Harness(通常在shell 中调用prove)依次运行一系列测试,并获得通过或失败的摘要。

    Test::More 也可以做一些更复杂的事情,比如将测试标记为 TODO(不要期望它们通过,但以防万一运行它们)或 SKIP(这些测试被破坏/可选,不要运行他们)。您可以声明您希望运行的测试数量,因此如果您的测试脚本中途死亡,则可以检测到。

    一旦您开始进行更复杂的测试,您可能会发现其他一些有用的 CPAN 模块 - 这里有一些示例,但还有很多 (many):

    Test::Exception - 测试您的代码是否抛出错误/不抛出任何错误
    Test::Warn - 测试您的代码是否产生警告
    Test::Deep - 深入比较对象。它们不必相同 - 您可以忽略数组排序、使用正则表达式、忽略对象类等。
    Test::Pod - 确保您的脚本有 POD(文档),并且它是有效的
    @ 987654328@ - 确保您的 POD 记录了模块中的所有方法/功能
    Test::DBUnit - 测试数据库交互
    Test::MockObject - 制作假装对象来控制测试环境

    【讨论】:

      【解决方案3】:

      一定要从这个页面开始:http://perldoc.perl.org/Test/Simple.html 并遵循对Test::Tutorial 的引用。

      【讨论】:

        【解决方案4】:

        如果您练习 TDD,您会注意到您的单元测试集发生了很大变化。 Test::Class 遵循 xUnit 模式 (http://en.wikipedia.org/wiki/XUnit)。

        对我来说,xUnit 的主要好处是将每个测试封装在方法中。框架以测试方法的名称命名每个断言,并增加了在每次测试之前和之后运行设置和拆卸方法的可能性。

        我也尝试过“perl-ish”的单元测试方式(仅使用 Test::More),但我发现它有点过时且麻烦。

        【讨论】:

        • 我也很喜欢 Test::Class。 Test::Class 的巧妙之处在于它只是构建基于 TAP 的测试的另一种方式——另一个框架,将其从“过程”转换为“面向对象”。它将“它的报告方式”与“它的运行方式”分开,让您可以在您的 Test::Class 对象中使用任何其他 Test::More 样式的测试步骤/测试函数。但它也不必用于基于 TAP 的测试——它恰好非常擅长它。您可以扩展 Test::Class 并覆盖许多默认行为,并简单地使用框架。
        【解决方案5】:

        一些反建议可能是为了:

        反推荐:

        不要为 Perl 使用 Test::Unit 系列测试包,例如 Test::Unit::AssertTest::Unit::TestCases

        原因:Test::Unit 似乎已被放弃。

        Test::Unit、Test::Unit::TestCases、Test::Unit::Assert 工作得很好(当我在 2015-2016 年使用它们时)。 Test::Unit 据说没有与 Perl 的测试任何协议 (TAP) 集成,尽管我发现这很容易修复。

        但是 Test::Unit 令人沮丧,因为许多其他 Perl 测试包,主要是使用 Test::Builder 构建的,例如 Test::More、Test::Most、Test::Exception、Test::Differences、Test ::Deep、Test::Warn 等不能与 Test::Unit 的面向对象测试方法很好地交互。

        一旦您调整了 Test::Unit 以与 Test::More 和 TAP 一起使用,您就可以混合使用 Test::Unit 测试和 Test::Builder 测试;但是这些其他软件包的好特性不适用于 OO 扩展。这也是使用 xUnit 样式测试的主要原因。

        据说 CPAN 的 Test::Class 允许“以 xUnit/JUnit 样式轻松创建测试类”——但我不确定我是否可以推荐这个。它在我看来肯定不像 xUnit - 不是 OO,像 is(VAL1,VAL2,TESTNAME) 这样的特殊名称而不是像 $test_object->assert_equals(VAL1,VAL2,TEST_ERR_MSG) 这样的 xUnit 样式名称。 Test::Class 确实具有自动检测所有带 :Test 注释的测试的令人愉快的功能,这与 xUnit 和 TEST::Unit::TestCase 使用内省来运行所有名为 test_* 的函数的方法相当。

        但是,底层包 Test::Builder 是面向对象的,因此更具有 xUnit 风格。不要被这个名字吓到——它不是工厂,它主要是一个带有测试断言方法的套件。尽管大多数人都继承自它,但如果您愿意,您可以直接调用它,例如$test_object->is(VAL1,VAL2,TESTNAME),通常您可以使用 Test::Builder 调用来解决程序包的限制,例如基于 Test::Builder 构建的 Test::More ——比如修复报告错误的调用堆栈级别。

        Test::Builder 通常使用单例样式,但可以创建多个对象。我不确定这些行为是否符合 xUnit 系列测试的预期。

        到目前为止,没有简单的方法来解决 Perl TAP 测试使用 TEST_NAMES 等限制,每个断言,没有层次结构,也没有区分 TEST_NAMES 和 TEST_ERROR_MESSAGES。 (错误报告级别有助于弥补这一缺陷。)

        也许可以创建一个适配器,使 Test::Builder 和 TAP 样式测试更加面向对象,这样您就可以重新基于 TAP 以外的东西(它记录比 TAP 更有用的信息 - 据说像 ANT 的 XML 协议) .我认为调整名称和/或缺失的概念将涉及进入 Test::Builder 或自省。

        【讨论】:

        • 谢谢,彼得。干净多了。当我浪费时间使用老化的软件时,我开始起泡。
        猜你喜欢
        • 1970-01-01
        • 2023-03-25
        • 1970-01-01
        • 1970-01-01
        • 2010-09-12
        • 1970-01-01
        • 2014-10-17
        • 1970-01-01
        • 2011-09-14
        相关资源
        最近更新 更多