生成随机输出的单元测试代码的最佳方法是什么?

时间:2010-06-18 09:30:21

标签: unit-testing statistics probability

具体来说,我有一个方法从列表中选择n个项目,其中%的符合一个标准,b%符合第二个,依此类推。一个简单的例子是选择5个项目,其中50%具有值为'true'的给定属性,50%'false'; 50%的时间方法返回2真/ 3假,其他50%,3真/ 2假。

从统计学上讲,这意味着超过100次运行,我应该得到大约250真/ 250假,但由于随机性,240/260是完全可能的。

对此进行单元测试的最佳方法是什么?我假设即使技术上300/200是可能的,如果发生这种情况,它应该可能无法通过测试。对于这样的案例,是否存在普遍接受的容忍度?如果是这样,您如何确定这是什么?

编辑:在我正在处理的代码中,我没有使用伪随机数生成器,或者强制它随时间平衡的机制,因为挑选出的列表是在不同的机器上生成。我需要能够证明,随着时间的推移,符合每个标准的平均项目数量将达到所需的百分比。

9 个答案:

答案 0 :(得分:22)

随机和统计数据在单元测试中不受青睐。单元测试应始终返回相同的结果。总是。不是主要的。

您可以做的是尝试删除正在测试的逻辑的随机生成器。然后你可以模拟随机生成器并返回预定义的值。


其他想法:

您可以考虑更改实现以使其更易于测试。尽量减少随机值。例如,您可以只获取一个随机值来确定与平均分布的偏差。这很容易测试。如果随机值为零,则应获得平均预期的精确分布。如果该值例如是1.0,则您会错过某个已定义因子的平均值,例如10%。您也可以实现一些高斯分布等。我知道这不是这里的主题,但如果您可以随意实现它,请考虑可测试性。

答案 1 :(得分:4)

根据您的统计信息,确定范围而不是特定的单个值。

答案 2 :(得分:4)

许多概率算法,例如科学计算使用pseudo-random number generators,而不是 true 随机数生成器。即使它们不是真正随机的,精心挑选的 - 随机数生成器也可以正常工作。

- 随机数生成器的一个优点是它们产生的随机数序列是完全可重现的。由于算法是确定性的,因此相同的种子将始终生成相同的序列。这通常是决定它们首先被选择的决定因素,因为实验需要重复,结果可重复。

这个概念也适用于测试。可以设计组件,以便您可以插入任何随机数源。为了进行测试,您可以使用始终播种的生成器。结果将是可重复的,适合于测试。

请注意,如果实际上需要 true 随机数,您可以仍然以这种方式进行测试,只要该组件具有可插入的随机数源。您可以将相同的序列(如果需要可能是真正随机的)重新插入同一个组件进行测试。

答案 3 :(得分:4)

在我看来,你想在这里测试至少三个不同的东西:

  1. 使用随机源生成输出的过程的正确性
  2. 随机源的分布是您期望的
  3. 输出的分布是您期望的
  4. 1应该是确定性的,您可以通过提供一组已知的“随机”值和输入并检查它是否产生已知的正确输出来对其进行单元测试。如果您构造代码以便随机源作为参数传递而不是嵌入代码中,这将是最简单的。

    2和3无法绝对测试。您可以测试一些选定的置信水平,但您必须为这些测试做好准备,以便在某些情况下失败。可能你真正想要注意的是测试3比测试2更频繁地失败,因为这表明你的算法是错误的。

    要应用的测试取决于预期的分布。对于2,您最有可能期望随机源均匀分布。对此有各种测试,具体取决于您希望的参与程度,例如参见Tests for pseudo-random number generators on this page

    3的预期分布将在很大程度上取决于您正在生产什么。问题中简单的50-50案例完全等同于testing for a fair coin,但显然其他案例会更复杂。如果你能弄清楚分布应该是什么,chi-square test反对它可能会有所帮助。

答案 4 :(得分:3)

这取决于您对测试套件的使用。如果你每隔几秒运行一次,因为你接受了测试驱动的开发和积极的重构,那么它不会虚假地失败是非常重要的,因为这会导致严重的中断并降低生产力,所以你应该选择一个几乎不可能实现的阈值达到一个良好的实施。如果您每晚运行一次测试并且有时间调查失败,那么您可能会更加严格。

在任何情况下都不应该部署会导致经常出现未经调查的失败的事情 - 这会破坏拥有测试套件的整个目的,并大大降低其对团队的价值。

答案 5 :(得分:2)

您应该在“单个”单元测试中测试结果的分布,即结果在任何单个运行中尽可能接近所需的分布。对于您的示例,2 true / 3 false是正确的,4 true / 1 false是不正常的结果。

您也可以编写执行该方法的测试,例如100次并检查分布的平均值是否足够“接近”所需的速率。这是一个临界案例 - 运行更大批量可能需要花费大量时间,因此您可能希望与“常规”单元测试分开运行这些测试。此外,正如Stefan Steinegger所指出的那样,如果你定义“足够接近”更严格,或者如果你过于宽松地定义阈值,这样的测试将不时失败。所以这是一个棘手的案例......

答案 6 :(得分:1)

我认为如果我遇到同样的问题,如果你有关于average / stddev等的统计数据,我可能会建立一个置信区间来检测异常。因此,在您的情况下,如果平均预期值为250,则使用正态分布在平均值附近创建95%置信区间。如果结果超出该间隔,则表示测试失败。

请参阅more

答案 7 :(得分:0)

为什么不重新考虑随机数生成代码,让单元测试框架和源代码都使用它?您正在尝试测试算法而不是随机序列吗?

答案 8 :(得分:0)

首先,您必须知道随机数生成过程应该产生什么样的分布。在您的情况下,您生成的结果为0或1,概率为-0.5。这描述了binomial distribution,其中p = 0.5。

考虑到n的样本大小,您可以构建(如早期的海报所示)围绕均值的置信区间。当n = 500时,你也可以做出关于获得概率的各种陈述,例如,240或更少的结果。

只要p不是很大或非常小,就可以对N大于20的值使用正态分布假设。维基百科的帖子对此有更多的了解。