我有一位同事为对象编写单元测试,这些对象用随机数据填充其字段。他的原因是它提供了更广泛的测试,因为它将测试许多不同的值,而普通测试仅使用单个静态值。

我给了他很多不同的反对理由,主要是:

  • 随机值意味着测试并不是真正可重复的(这也意味着如果测试可能随机失败,它可以在构建服务器上这样做并破坏构建)
  • 如果它是一个随机值并且测试失败,我们需要 a) 修复该对象 b) 强迫自己每次都测试该值,所以我们知道它是有效的,但由于它是随机的,我们不知道该值是什么

另一位同事补充道:

  • 如果我正在测试异常,随机值将无法确保测试最终达到预期状态
  • 随机数据用于清理系统和负载测试,而不是用于单元测试

其他人可以添加我可以给他的其他理由让他停止这样做吗?

(或者,这是一种可以接受的编写单元测试的方法,而我和我的其他同事错了?)

有帮助吗?

解决方案

有一个妥协。你的同事确实在做某事,但我认为他做错了。我不确定完全随机的测试是否非常有用,但它肯定不是无效的。

程序(或单元)规范是一种假设,即存在一些满足它的程序。该程序本身就是该假设的证据。单元测试应该是尝试提供反证据来反驳程序按照规范运行。

现在,您可以手动编写单元测试,但这确实是一项机械任务。它可以自动化。您所要做的就是编写规范,机器可以生成大量试图破坏您的代码的单元测试。

我不知道你用的是什么语言,但请看这里:

爪哇http://functionjava.org/

Scala(或 Java)http://github.com/rickynils/scalacheck

哈斯克尔http://www.cs.chalmers.se/~rjmh/QuickCheck/

。网:http://blogs.msdn.com/dsyme/archive/2008/08/09/fscheck-0-2.aspx

这些工具会将格式良好的规范作为输入,并自动生成所需数量的单元测试以及自动生成的数据。他们使用“收缩”策略(您可以调整)来找到最简单的测试用例来破坏您的代码并确保它很好地覆盖了边缘情况。

测试愉快!

其他提示

这种测试称为 猴子测试. 。如果做得好,它可以从真正黑暗的角落里熏出虫子。

为了解决您对再现性的担忧:解决这个问题的正确方法是记录失败的测试条目,生成一个单元测试,该测试探测 全家人 具体错误;并在单元测试中包含导致初始故障的一个特定输入(来自随机数据)。

这里有一个中间方案,它有一些用处,就是用一个常量来为你的 PRNG 播种。这允许您生成可重复的“随机”数据。

就我个人而言,我确实认为(恒定)随机数据在测试中很有用——在你认为你已经完成了所有仔细考虑的角落之后,使用 PRNG 的刺激有时可以找到其他东西。

在书里 美丽的代码, 有一章叫做“美丽的测试”,他在其中介绍了一个测试策略 二分查找 算法。有一段叫做“测试的随机行为”,其中他创建了随机数组来彻底测试算法。您可以在 Google 图书上在线阅读其中的一些内容, 第95页, ,但这是一本值得拥有的好书。

所以基本上这只是表明生成随机数据进行测试是一个可行的选择。

对于查看测试的人来说,一个优点是任意数据显然并不重要。我见过太多涉及数十条数据的测试,很难判断什么需要这样,什么恰好是这样。例如。如果使用特定邮政编码测试地址验证方法,并且所有其他数据都是随机的,那么您可以非常确定邮政编码是唯一重要的部分。

如果您正在进行 TDD,那么我认为随机数据是一种很好的方法。如果您的测试是用常量编写的,那么您只能保证您的代码适用于特定值。如果您的测试随机导致构建服务器失败,则测试的编写方式可能存在问题。

随机数据将有助于确保未来的任何重构都不会依赖于魔法常数。毕竟,如果您的测试就是您的文档,那么常量的存在是否意味着它只需要对这些常量起作用?

我有点夸张,但我更喜欢将随机数据注入到我的测试中,作为“此变量的值不应影响此测试的结果”的标志。

我想说的是,如果你使用随机变量,然后根据该变量分叉你的测试,那么那就是一种味道。

  • 如果它是一个随机值并且测试失败,我们需要 a) 修复该对象 b) 强迫自己每次都测试该值,所以我们知道它是有效的,但由于它是随机的,我们不知道该值是什么

如果您的测试用例没有准确记录正在测试的内容,也许您需要重新编码测试用例。我总是希望拥有可以参考测试用例的日志,以便我确切地知道导致失败的原因,无论是使用静态数据还是随机数据。

你的同事正在做 模糊测试, ,虽然他不知道。它们在服务器系统中特别有价值。

我赞成随机测试,并且我编写了它们。然而,它们是否适合特定的构建环境以及它们应该包含在哪些测试套件中是一个更微妙的问题。

在本地运行(例如,在您的开发盒上过夜)随机测试发现了明显和模糊的错误。那些晦涩难懂的东西太神秘了,我认为随机测试确实是唯一能将它们清除掉的现实方法。作为一项测试,我通过随机测试发现了一个很难发现的错误,并让六名熟练的开发人员检查了该错误发生的功能(大约十几行代码)。没有人能够检测到它。

您反对随机数据的许多论点都是“测试不可重复”。但是,编写良好的随机测试将捕获用于启动随机种子的种子并在失败时输出它。除了允许您手动重复测试之外,这还允许您轻松创建新测试,通过对该测试的种子进行硬编码来测试特定问题。当然,手动编写覆盖该情况的显式测试可能会更好,但是懒惰也有其优点,这甚至允许您从失败的种子中自动生成新的测试用例。

然而,你提出的我无法争论的一点是,它破坏了构建系统。大多数构建和持续集成测试都希望测试每次都做同样的事情。因此,随机失败的测试会造成混乱,随机失败并指责无害的更改。

那么,解决方案是仍然运行随机测试作为构建和 CI 测试的一部分,但是 使用固定种子运行固定迭代次数. 。因此,测试总是做同样的事情,但仍然探索一堆输入空间(如果您运行它进行多次迭代)。

在本地,例如,当更改相关类时,您可以自由地运行它进行更多迭代或与其他种子一起运行。如果随机测试变得更加流行,您甚至可以想象一套特定的测试,这些测试已知是随机的,可以使用不同的种子运行(因此随着时间的推移覆盖范围会增加),并且失败并不意味着同样的事情作为确定性 CI 系统(即,运行与代码更改并不 1:1 关联,因此当事情失败时,您不会指责特定的更改)。

对于随机测试,尤其是写得好的随机测试,有很多话要说,所以不要太快地忽略它们!

您能否生成一些随机数据一次(我的意思是恰好一次,而不是每次测试运行一次),然后在之后的所有测试中使用它?

我绝对可以看到创建随机数据来测试那些您没有想到的情况的价值,但您是对的,拥有可以随机通过或失败的单元测试是一件坏事。

您应该问自己测试的目标是什么。
单元测试 是关于验证逻辑、代码流和对象交互。使用随机值试图实现不同的目标,从而降低测试重点和​​简单性。出于可读性原因(生成 UUID、ids、密钥等),这是可以接受的。
特别是对于单元测试,我什至不记得这种方法是否成功发现了问题。但我看到许多确定性问题(在测试中)试图巧妙地处理随机值,主要是随机日期。
模糊测试是一种有效的方法 集成测试端到端测试.

如果您在测试中使用随机输入,则需要记录输入,以便查看值是什么。这样,如果您遇到一些边缘情况,您 编写测试来重现它。我从人们那里听到了不使用随机输入的相同原因,但是一旦您深入了解用于特定测试运行的实际值,那么这就不是什么大问题了。

“任意”数据的概念作为表示某事物的一种方式也非常有用。 不是 重要的。我们想到了一些验收测试,其中存在大量与当前测试无关的噪声数据。

根据您的对象/应用程序,随机数据将在负载测试中占有一席之地。我认为更重要的是使用明确测试数据边界条件的数据。

我们今天才遇到这个问题。我想了 伪随机 (因此它在大小方面看起来像压缩的音频数据)。I TODO'd 我也想要 确定性的. 。rand() 在 OSX 上与在 Linux 上不同。除非我重新播种,否则它随时可能发生变化。因此我们将其更改为确定性但仍然是伪随机的:该测试是可重复的,就像使用预设数据一样(但编写更方便)。

这是 不是 通过代码路径进行一些随机强力测试。这就是区别:仍然是确定性的,仍然是可重复的,仍然使用看起来像真实输入的数据来对复杂逻辑中的边缘情况运行一组有趣的检查。仍然是单元测试。

这仍然符合随机条件吗?我们边喝啤酒边聊吧。:-)

我可以设想三种解决测试数据问题的方案:

  • 使用固定数据进行测试
  • 用随机数据进行测试
  • 生成随机数据 一次, ,然后将其用作您的固定数据

我建议做 上述所有的. 。也就是说,编写可重复的单元测试,其中包括使用您的大脑计算出的一些边缘情况以及仅生成一次的一些随机数据。然后编写一组运行的随机测试 还有.

永远不要指望随机测试能够发现可重复测试遗漏的东西。您应该致力于通过可重复的测试覆盖所有内容,并将随机测试视为一种奖励。如果他们发现了什么,那应该是你无法合理预测的;一个真正的怪人。

当你的人无法查看他是否已修复它时,如何再次运行测试?IE。他失去了测试的可重复性。

虽然我认为在测试中抛出大量随机数据可能有一定的价值,但正如其他回复中提到的,它比其他任何东西都更属于负载测试的标题。这几乎是一种“希望测试”的做法。我认为,实际上,你的人根本没有考虑他要测试的内容,并通过希望随机性最终会陷入一些神秘的错误来弥补这种思考的缺乏。

所以我对他的论据是他很懒。或者,换句话说,如果他不花时间去理解他正在尝试测试的内容,则可能表明他并不真正理解他正在编写的代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top