【问题标题】:Unit tests for automatically generated code: automatic or manual?自动生成代码的单元测试:自动还是手动?
【发布时间】:2010-10-25 03:47:12
【问题描述】:

我知道similar questions 之前有人问过,但他们并没有我正在寻找的信息——我不是在问如何生成单元测试的机制,而是问这是否是个好主意。

我用 Python 编写了一个模块,其中包含表示物理常数和测量单位的对象。许多单位是通过向基本单位添加前缀来形成的 - 例如。从m 我得到cm、dm、mm、hm、um、nm、pm 等。对于s、@987653321@、@987653331@ 等也是如此@ 等。当然,我已经编写了一个函数来执行此操作,因为最终结果是超过 1000 个单独的单元,手动将它们全部写出来会很痛苦;-) 它的工作原理是这样的(不是实际的代码):

def add_unit(name, value):
    globals()[name] = value
    for pfx, multiplier in prefixes:
        globals()[pfx + name] = multiplier * value

add_unit('m', <definition of a meter>)
add_unit('g', <definition of a gram>)
add_unit('s', <definition of a second>)
# etc.

当我想为这些单元(没有双关语)编写单元测试时,问题就出现了,以确保它们都具有正确的值。如果我编写的代码分别为每个单元自动生成一个测试用例,那么单元生成函数中的任何问题都可能会出现在测试生成函数中。但是考虑到替代方案(手动写出所有 1000 多个测试),我是否应该继续编写测试生成函数,非常仔细地检查它并希望它能正常工作?或者我应该只测试一系列单位(m、cm、dm、km、nm、um 以及所有其他米的倍数),足以使确定单位生成功能似乎正在工作?还是别的什么?

【问题讨论】:

    标签: python unit-testing code-generation


    【解决方案1】:

    您认为自动生成测试用例的弱点是正确的。测试的用处在于采用两条不同的路径(您的代码和您自己的心理推理)来得出应该是相同的答案——如果您两次使用相同的路径,则没有任何测试。

    总结:永远不要编写自动生成的测试,除非生成测试结果的算法比您正在测试的算法简单得多。 (排序算法的测试是自动生成测试的一个例子,因为它很容易验证数字列表是否按排序顺序。另一个很好的例子是解谜程序as suggested by ChrisW评论。在这两种情况下,自动生成都是有意义的,因为验证给定解决方案的正确性比生成正确的解决方案要容易得多。)

    我对您的案例的建议:手动测试一小部分具有代表性的可能性。

    [澄清:某些类型的自动化测试是适当且非常有用的,例如fuzzing。我的意思是,为生成的代码自动生成单元测试是没有帮助的。]

    【讨论】:

    • 我不完全同意。例如,假设要测试的代码是解析器和编译器:要自动生成的测试可能是要编译的各种代码片段。解析比较复杂,但是自动生成各种(很多)代码片段相对容易,代码检查会告诉你自动生成的代码片段是否有效。
    • @ChrisW:我希望在“模糊测试”下捕捉到那种自动化测试,但也许这个术语太具体了。但是自动生成许多已知正确的片段(例如,通过随机扩展语法中的规则)并测试解析器是否处理它们而不会抱怨的一般想法是一个很好的想法。
    【解决方案2】:

    如果您自动生成测试:

    • 您可能会发现阅读所有测试(检查它们的正确性)比手动编写它们要快。

    • 它们也可能更易于维护(更容易编辑,如果您想稍后编辑它们)。

    【讨论】:

    • 恕我直言,生成测试的逻辑几乎没有被测试的逻辑复杂(可能更复杂)——如果 it 足够简单地“检查”正确性,那为什么不测试原始代码呢?
    • 我不是建议你检查生成测试的逻辑:我建议你检查测试本身。 “通过检查”不总是您验证测试实施的方式吗?至于“为什么不检查原始代码?”,很明显,当您编写它时,您也会这样做……但是进行自动化测试的一个原因是您可以廉价地重新运行它们(作为回归测试):昂贵写作,检查成本高,但重播成本低;如果您仅依靠检查来测试原件,那么每次原件更改时都需要(昂贵地)重新检查。
    • 对不起,ChrisW,我误读了您的第一点。如果所有“生成”只是将一些样板添加到手动策划的测试列表中(例如,将手动准备的以制表符分隔的文本文件的每一行列出参数和预期的返回值转换为调用函数并测试结果的 Perl 语句) 这很好,但我仍然认为,如果您的测试生成实际上从每个人工输入单元生成 多个 测试,它会削弱测试。
    • OTOH 我绝对可以看到回归测试的用处。即使测试本身容易受到可能存在于被测试代码中的相同错误的影响,确保测试行为不会随着时间而改变也是有价值的。 +1。
    • 嗯,通常你是对的,从某种意义上说,“每个人工提供的输入单元”提供了一些价值,但自动乘以该输入可能不会提供额外的价值。但是,有时自动生成测试用例可能很有用。考虑一个解谜程序:您可能会自动生成(许多)谜题,并为每个谜题测试解决方案是否有效。有时最好为已知的(人类测试人员知道的)边缘案例手动生成谜题(即测试用例);偶尔(取决于问题领域)您可能需要 1000 次测试以进行覆盖。
    【解决方案3】:

    我会说最好的方法是对生成进行单元测试,作为单元测试的一部分,您可能会抽取一个生成的结果样本(仅足以让测试测试一些您认为与其他场景有显着不同的东西) 并将其置于单元测试下,以确保生成正常工作。除此之外,以自动化方式定义每个场景几乎没有单元测试价值。将一些功能测试组合在一起可能具有功能测试价值,这些测试会运行生成的代码以执行您想到的任何目的,以便为各种潜在单元提供更广泛的覆盖范围。

    【讨论】:

      【解决方案4】:

      只编写足够的测试以确保您的代码生成正确(刚好足以驱动命令式代码的设计)。声明式代码很少中断。你应该只测试可以破坏的东西。声明性代码中的错误(例如您的案例和例如用户界面布局)通过探索性测试更好地发现,因此为它们编写大量的自动化测试是浪费时间。

      【讨论】:

        猜你喜欢
        • 2010-09-10
        • 1970-01-01
        • 2021-03-01
        • 2010-09-10
        • 1970-01-01
        • 2011-02-18
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        相关资源
        最近更新 更多