• 当一个数不是数字时:随机测试生成器的好处(译)


    摘要:

    我们希望在划分我们的测试时,我们将考虑所有的场景,但是太容易忽略不常用的用例。这就是随机测试生成器的好处。我们可能在测试几十个测试用例后感觉很舒适;这些工具能生成几百个。随着更多的东西被扔到墙上,一些有趣的东西更有可能被粘在墙上。

    在第一个尝试FsCheck和基于属性的测试后,我恼火了。

    Haskell编程语言已经存在一段时间了,然而我从来不用它。吸引我注意的是一个名为QuickCheck的工具和它引进的算法。因为我一直工作在.NET领域,我采选用FsCheck做研究,一个F#端口的QuickCheck。我的恼火来自于演示list集合的属性。

    当我们反转一个列表,然后再反转一次,我们希望得到初始列表,对吗?在不考虑这个列表有多长或者包含什么,这个属性应该是真的。想象一下当我第一次用FsCheck测试失败时,我有多么惊讶。第一次带有整数列表的测试通过了,但是当我把它变成持有浮点型时,它失败了。

    FSCheck生成随机值填入列表,然后检查属性是否被测试持有。它做了很多很多次测试,为了查找失败的值的组合。在整数的用例中,它不仅仅是我们首先考虑到的1、2、3……,还有会被存储成固定浮点整数、零和负数的最大和最小整数值。我们也需要考虑空列表。FsCheck所有这些都做了。为什么当我考虑浮点数时,它失败了?

    解释来自于对浮点值的定义。他们采用一种位的模式,并将该模式解释为一个带有小数点的数字。无论如何,并不是所有可能的比特组合都可以用这种方式进行有意义的解释。我们把这些麻烦的模式NaN定义为:不是一个数。令我惊讶的是,NaN变量可以是逐位相同的,但不相等。让他们明白这一点。这似乎合情合理,但却有违直觉——而且很容易被忽视。

    我们希望在设计测试的时候,考虑所有这样的病态情况,但是很容易忽略像NaN这样的东西。这就是像FsCheck这样的随机测试生成器的好处。我们在测试几十个测试用例后可能感觉很舒服。FsCheck生成几百个。随着更多的东西被扔到墙上,一些有趣的东西更有可能粘在墙上。

    在FsCheck发现故障之后,它将采取第二步:它试图减少引发问题所需的数据量。我不详细讨论这第二步,因为我的观点是,看到像我刚才用NaN描述的那样的错误有助于我们更深入地思考代码库在做什么——尤其是在将它的测试缩减到最小的形式之后。

    在浮点值列表的情况下,我们可能认为NaN业务是假的。我们这样做甚至可能是正确的。但是运行测试的目的是让我们考虑代码库。也许我们应该在系统的边缘添加一些东西来使NaN值出现在系统之外,或者我们应该确保NaN不能从系统内部的任何转换中产生。

    我们的代码有缺陷,因为我们对它的思考存在盲点。即使是世界上最大的自动测试用例生成器也会有盲点。令人高兴的是,机器和人类有不同的盲点。

        像FsCheck或QuickCheck这样的工具利用了一种不同的测试范例,利用机器的优势来弥补我们的不足。机器可以通过算法制定出大量的测试数据集,这些数据集手工组装起来既昂贵又枯燥。但是他们需要基于属性的测试来消耗所有的数据。

    当我还是一名新程序员时,我并不欣赏这一点。我正在研究一个统计应用程序,它严重依赖于贝叶斯统计和概率。有一些事情你可以依靠条件概率。首先,概率总是在0到1之间。另一方面,当我们总结所有条件时,我们应该得到一个。无论我们在代码中添加什么,这些属性是我们可以自动检查的。

    同样,我最近与一些朋友进行了一次对话,他们正在构建一个计费系统。我问了一些问题:所有的项目都是正数的吗?我们是否知道许多行项目的小计将超过更少项目的小计?在不考虑求和顺序的情况下,总数是否会发生变化?我问,还有哪些属性是不变的,我们可以断言。每个这样的不变量都可以作为基于属性的测试的基础。跟你打赌,我肯定会给这个系统一些负的价格。

    广泛的属性测试可以教会我们很多关于代码库的知识。测试从不确定代码库是无错误的,但是测试确实缩小了错误可以隐藏的范围。为了学得更多,我们必须理解测试的作用和它们所显示的内容。这可能意味着我们应该更多地关注理解我们的测试,而不是仅仅增加少量理解的测试的乘法。就像FsCheck将大型失败测试简化为更简单的表单一样,我们也应该定期检查我们的测试套件。

    是否每个测试告诉我们的不仅仅是重现过去几年bug报告的环境?这些测试是否可以重新组织以阐明我们为什么关心它们?我们的目标应该是策划测试,以最大限度地从每个测试的成功或失败中得到理解。

    当我对浮点的基于属性的测试失败时,NaN让我感到惊讶。这并不是由于列表倒转的故障;它失败的原因是测试中固有的关于如何比较浮点数的假设。这提醒我们寻找与我们的假设相矛盾的数据。我们在测试中加入的惊喜越多,我们的系统就会变得越健壮。这需要结合多种不同的范例和功能来利用每种范例和功能的优势。

    被NaN吓到似乎是个坏消息。它让我们陷入了一个失败的测试,这个测试只与我们正在测试的代码间接相关。但是这样做会让我们想起一些我们可能已经忘记的软件。这样的提醒会把惊喜变成好消息。

  • 相关阅读:
    list和set的区别
    tcp与udp协议的区别
    c3p0的数据库连接池
    mysql的连接
    HAVING 的使用 及笛卡尔积
    break-跳出内循环
    求素数(范围自改)
    1-100累加
    1-100累乘
    类实例:飞机大战
  • 原文地址:https://www.cnblogs.com/fengye151/p/11165603.html
Copyright © 2020-2023  润新知