• 0
  • 1
分享
  • 当一个数不是数字时:随机测试生成器的好处
  • 恬恬圈 2019-07-18 13:34:03 字数 2082 阅读 1357 收藏 1

摘要:

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


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


本文未经授权禁止转载,如需转载请于51Testing小编联系。

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   摘要:为有效开展数据报送测试,本文提出一种数据交互驱动测试的方法和经验,通过阐述数据交互的不同测试对象对应的具体测试方法和测试通过准则,为后续数据交互类测试工作提供借鉴和参考。  一、数据报送测试面临的挑战  数据报送系统的核心工作是满足接口要求,完成数据的报送工作。与传统的页面级-功能级-流程级的测试方法不同,数据报送系统测试更关注于交互数据流程测试、交互数据质量和交互数据的时效性测试。要求测试人员熟悉业务规则、交互规范、校验规则和SQL校验方法,既能进行数据SQL验证也能进行报文的数据测试。  二、数据交互驱动测试方法  针对以上挑战,结合以往项目测试方法,本文提出了一种数据交互驱动测...
            13 13 1492
            分享
          • 作为一名多年的测试人员,对测试这个岗位的存在,也有自己的一点拙见。纵观现在的互联网公司,不管是国际巨头,行业巨头,或初创型公司,测试的岗位都必不可少。但不同的公司之间,却又有很多的差异性,拿测试开发比来说:在 Google 公司,测试开发比为 1:10;但微软则能达到 1:1 甚至更高;笔者现在所在的公司,大概是到 1:3 左右。其实每个公司的配比,都是视本公司的业务形态/协作方式决定的,单纯靠配比来决定什么,也是不科学的。微软的一些测试人员需要写单元测试,相当于测试开发的角色,写出来的东西去测试开发写出来的代码,加之微软产品性质比如复杂操作系统,服务器产品之类的,需要的测...
            0 0 1854
            分享
          • 在之前的文章和视频中,我分享过Groovy语法中def关键字的基本使用方法。当时对def理解是:不定类型变量,资料中也有说是无类型变量,感觉两个意思大差不差,就是不显式声明对象类型。基本使用方式如下:def a = 1def b = "FunTester"类似这样的方式,当时Java新版也已经支持了var可以替代一些显式声明变量类型的代码,但是除了使用范围上,Groovy的def还是明显优于var,而且功能上也是强不少(当然有人理解为弱不少,后续讲解)。本质区别在于Groovy的def不仅可以替代显式的类型声明,还...
            0 0 833
            分享
          •   什么是自动化测试  在软件测试领域,有两种测试技术:手动测试和自动测试。两种方法都旨在执行测试用例,然后将实际结果与预期结果进行比较。  概括来讲手动测试是一种人工执行操作的测试技术,可确保软件代码完成应有的功能。那么什么是自动化测试呢?相反,这是一种自动运行测试技术,管理测试数据以及利用结果来提高软件质量的实践。  对于一名测试工程师而言,连续的开发周期需要重复执行相同的测试用例组成的测试套件。如果每一次都手动执行此过程,可能会非常重复且耗时,很容易让人产生疲倦感。但是通过利用测试自动化工具,可以更轻松地编写测试套件,按需重手动执行,减轻人为干预并提高测试ROI(自动化测试的投资回报率)...
            1 1 2214
            分享
          • 读者提问:软件测试行业是如何发展起来的,软件测试的定义是什么呢 ?阿常回答:从软件开发一开始就有软件测试了,起初的软件测试严格来说,不能算作真正的软件测试,是由开发人员完成的 “调试”。1975年《软件数据选择的原理》将软件测试定义为 :“ 证明软件测试工作是正确 ” 的活动,即 “ 证实 ”。1979年《软件测试艺术》将软件测试定义为 :“ 发现错误而执行的活动 ”,即 “ 证伪 ”。1983年《软件测试完全指南》将软件测试定义为:“ 测试是以评价一个程序或者系统属性为目标的任一活动,测试是对软件质量的度量。”,即 “ 预防 ”。2002年《系统的软件测试》将软件测试定义为:“ 测...
            0 0 1505
            分享
      • 51testing软件测试圈微信