在单元测试中使用随机值有哪些缺点?

时间:2010-08-09 15:42:40

标签: unit-testing

我想知道在某些单元测试中使用随机值的缺点是什么。


我说的是一个大规模的系统,有许多服务器和高容量的非确定性输入。当我说非确定性时,我正在谈论发送的消息,你抓住你能做到的,尽你所能。 有许多类型的消息,因此输入可能非常复杂。 我无法想象为这么多场景编写代码,而简单的非随机(确定性)消息的生成器也不够好。 这就是为什么我想要进行随机单元测试或服务器测试,以便在出现故障时写入日志。 而且我更喜欢单元测试而不是随机注射器,因为我希望它作为夜间构建自动化测试的一部分运行。

11 个答案:

答案 0 :(得分:38)

<强>缺点

首先,它使测试更加复杂并且稍微难以调试,因为您无法直接看到所有输入的值(尽管总是可以选择生成测试用例作为代码或数据)。如果你正在做一些半复杂的逻辑来生成随机测试数据,那么这个代码也有可能存在错误。测试代码中的错误可能很痛苦,特别是如果开发人员立即认为错误是生产代码。

其次,通常不可能对预期答案具体说明。如果你根据输入得知答案,那么你就有可能只是在试验中的逻辑(考虑一下 - 如果输入是随机的,你怎么知道预期的输出?)结果,你可能必须交换非常具体的断言(值应该是x)以获得更一般的完整性检查断言(值应该在y和z之间)。

第三,除非有广泛的输入和输出,否则通常可以在标准单元测试中使用精心选择的值覆盖相同的范围,而复杂性较低。例如。选择数字-max,( - max + 1), - 2,-1,0,1,2,max-1,max。 (或算法的任何有趣内容)。

<强>上升空间

如果使用正确的目标,这些测试可以提供非常有价值的补充测试通过。我已经看到了相当多的代码,当被随机生成的测试输入锤击时,由于不可预见的边缘情况而扭曲。我有时会添加一个额外的集成测试过程,它会生成一堆测试用例。

其他技巧

如果您的某个随机测试失败,请隔离“有趣”值并将其推广到独立的单元测试中,以确保您可以修复该错误,并且在签入之前永远不会回退。

答案 1 :(得分:8)

他们是随机的。

(即使你的代码被破坏,你的测试可能会随机工作。)

答案 2 :(得分:7)

此外,您将无法多次重现测试。单位测试应该与给定的参数完全相同。

答案 3 :(得分:3)

结果不可重复,并且根据您的测试,您可能不知道导致代码失败的特定条件(从而使调试变得困难)。

答案 4 :(得分:3)

让单元测试具有100%可重复性并包含所有边缘情况要好得多。例如,测试零,负数,正数,数字太大,数字太小等。如果你想包括除了所有边缘情况和正常情况之外的随机值的测试。但是,我不确定你会花多少时间从中获益。拥有所有正常案例和边缘案例应该处理所有事情。剩下的就是“肉汁”。

答案 5 :(得分:2)

随机单元测试是使用螺丝刀锤击钉子。问题不在于螺丝刀坏了;问题是你是否正在使用错误的工具来完成工作。单元测试的目的是在你破坏某些东西时提供即时反馈,这样你就可以在那里修复它。

让我们说你做了一个改变,我们称之为BadChange。 BadChange引入了一个错误,您的随机测试有时会捕获,有时不会捕获。这一次,测试没有抓住它。 BadChange被全部清除并进入代码库。

后来,有人做了另一个改变,GoodChange。 GoodChange是百分之百的罚款。但是这一次,你的随机测试会抓住BadChange引入的bug。现在,GoodChange被标记为一个问题,编写它的开发人员将试图弄清楚为什么这种无害的变化会导致问题。

随机测试有助于不断探究整个应用程序的问题,而不是验证个别更改。它应该位于一个单独的套件中,并且运行不应该与代码更改相关联;即使没有人做出改变,随机测试仍然可能会遇到一些以前错过的奇怪的错误。

答案 6 :(得分:1)

正如其他人所建议的那样,它会让你的测试变得不可靠,因为你不知道它内部发生了什么。这意味着它可能适用于某些情况,而不适用于其他情况。

如果您已经了解了要测试的值范围,那么您应该(1)为该范围中的每个值创建不同的测试,或者(2)循环该组值并进行制作每次迭代的断言。一个快速,相当愚蠢的例子......

for($i = 0; $i < 10; $i++)
  $this->assertEquals($i + 1, Math::addOne($i));

你可以用字符编码做类似的事情。例如,循环遍历ASCII字符集,并针对您的一个文本操作函数测试所有这些疯狂字符。

答案 7 :(得分:0)

您需要记住在验证过程中生成的随机数。

实施例。

Username= "username".rand();
Save_in_DB("user",Username); // To save it in DB
Verify_if_Saved("user",Username); 

答案 8 :(得分:0)

我认为,当与等价分区一起使用时,生成随机输入值可能是一种可靠的测试技术。 这意味着,如果您对输入空间进行分区,然后从等价类中随机选择值,那么您就可以了:相同的覆盖范围(其中任何一个,包括语句,分支,所有用途等)。 这假设你的等价分区程序是合理的。 此外,我建议边界值分析与等价分区和随机生成的输入配对。

最后,我还建议您考虑要检测的缺陷类型:某些测试技术可以解决特定类型的缺陷,这些缺陷可能很难(并且只是偶然)被其他技术检测到。一个例子:死锁条件。

总之,我认为生成随机值并不是一种坏习惯,特别是在某些系统(例如Web应用程序)中,但它只能解决现有缺陷的一部分(就像任何其他技术一样),并且应该注意因此,通过适当的活动来补充他/她的质量保证过程。

答案 9 :(得分:0)

好处:当你的其他测试涵盖所有不变量时,它们会显示出来。您是否希望CI服务器运行非确定性测试是另一个问题。鉴于我发现https://www.artima.com/shop/scalacheck非常有用,我不打算从现在开始没有它。我们假设您正在实施模式匹配算法。你真的知道所有不同的角落案件吗?我没有。随机输入可以将它们冲洗掉。

答案 10 :(得分:0)

尚未提及的其他Downside是您的测试可能会间歇性地随机失败,尤其是当您随机生成多个测试变量时,它们会形成一个复杂且有时难以处理的依赖关系。请参见示例here

调试这些是背后的正确痛苦,有时候(旁边)是不可能的。

此外,通常很难判断您的测试实际测试的内容(以及它是否测试过任何内容)。

在我的公司历史上,我们在多个级别使用随机测试(单元,集成,单一服务测试),这似乎是一个好主意 - 它可以节省代码,空间和时间,允许在一次测试中测试多个场景。

但是,当我们(甚至过去的历史性和可靠性)测试开始随机失败时,越来越多的事情成为我们发展的一个棘手问题 - 并且修复这些是劳动密集型的。