使用返回随机结果的函数进行unit testing

我不认为这是特定于一种语言或框架,但我使用xUnit.net和C#。

我有一个函数,返回一定范围内的随机date。 我通过一个date,返回date总是在给定date之前的1到40年的范围内。

现在我只是想知道是否有一个很好的方法来unit testing。 最好的方法似乎是创build一个循环,并让该函数运行100次,并断言这100个结果中的每一个都在期望的范围内,这是我目前的方法。

我也意识到,除非我能够控制我的随机生成器,否则将不会有一个完美的解决scheme(毕竟,结果是随机的),但我不知道当你必须testing返回随机结果的function一定范围?

除了testing函数返回所需范围内的date之外,还要确保结果分布均匀。 您所描述的testing将传递一个函数,它只是返回您发送的date!

因此,除了多次调用该函数并testing结果是否保持在期望的范围之外,我还会尝试评估分布,也许通过将结果放在桶中并检查桶之后的桶的结果数量大致相等完成。 您可能需要超过100次调用来获得稳定的结果,但这听起来不像一个昂贵的(运行时间)函数,所以您可以轻松地运行它几次K次迭代。

我之前遇到过一个问题,那就是非统一的“随机”function,它们可能是一个真正的痛苦,值得早期进行testing。

模拟或伪造随机数发生器

做这样的事情…我没有编译它,所以可能会有一些语法错误。

public interface IRandomGenerator { double Generate(double max); } public class SomethingThatUsesRandom { private readonly IRandomGenerator _generator; private class DefaultRandom : IRandomGenerator { public double Generate(double max) { return (new Random()).Next(max); } } public SomethingThatUsesRandom(IRandomGenerator generator) { _generator = generator; } public SomethingThatUsesRandom() : this(new DefaultRandom()) {} public double MethodThatUsesRandom() { return _generator.Generate(40.0); } } 

在你的testing中,只是假冒或模拟出IRandomGenerator返回jar装的东西。

我认为你testing的这个问题有三个不同的方面。

第一个:我的algorithm是正确的吗? 也就是说,给定一个function正常的随机数发生器,它会产生在整个范围内随机分布的date吗?

第二个:algorithm是否正确处理边界情况? 也就是说,当随机数发生器产生最高或最低的允许值时,是否有任何事情中断?

第三个:我的algorithm的实现工作? 也就是说,给定一个已知的伪随机input列表,是否产生了预期的伪随机date列表?

前两件事情不是我要build立到unit testing套件中的东西。 他们是我devise系统时要certificate的东西。 正如daniel.rikowski所build议的那样,我可能会通过编写一个testing工具来完成这个工作,这个testing工具可以产生数十万个date并进行卡方检验。 我还要确保这个testing工具不会终止,直到它处理了两个边缘情况(假设我的随机数范围足够小,以至于我可以逃脱这个)。 而且我会logging下来,所以任何人想要改进algorithm都会知道这是一个突破性的改变。

最后一个我做unit testing的东西。 我需要知道的是,没有任何东西能够渗透到破坏其algorithm实现的代码中。 当发生这种情况时,我会得到的第一个信号是testing失败。 然后,我会回到代码,发现别人认为他们正在修理一些东西,而不是打破它。 如果有人确定了algorithm,那么他们也会修正这个testing。

您不需要控制系统来确定结果。 你正确的做法是:决定什么是重要的function的输出和testing。 在这种情况下,结果在40天的范围内是非常重要的,而且您正在testing。 它也不总是返回相同的结果也很重要,所以也要testing一下。 如果你想更有趣,你可以testing结果通过某种随机性testing。

通常我使用你的build议方法:控制随机生成器。 使用默认种子对其进行初始化以进行testing(或者通过代理返回适合我的testing用例的代理来replace它),所以我具有确定性/可testing的行为。

如果你想检查随机数的质量(独立性)有几种方法可以做到这一点。 一个好方法是卡方检验 。

根据您的函数如何创build随机date,您可能还需要检查非法date:不可能的闰年或30天月份的第31天。

当然,使用一个固定的种子随机数发生器将工作得很好,但即使如此,你只是试图testing,你不能预测。 没关系。 这相当于有一堆固定的testing。 但是,记住 – testing什么是重要的,但不要试图testing所有的东西。 我相信随机testing是一种尝试testing一切的方法,而且效率不高(或者很快)。 在遇到错误之前,您可能需要运行大量的随机testing。

我试图在这里得到的是,你应该简单地为你的系统中find的每个bug编写一个testing。 您可以testing边缘情况,以确保即使在极端条件下您的函数仍在运行,但如果不花费太多时间,或者unit testing运行缓慢,或者仅仅是浪费处理器周期,那么这是最好的select。

不具有确定性行为的方法不能被适当地进行unit testing,因为结果会因执行而异。 解决这个问题的一个方法是为随机数发生器生成一个unit testing的固定值。 您还可以提取date生成类的随机性(从而应用单一责任原则 ),并为unit testing注入已知值。

我会build议覆盖随机function。 我在PHPunit testing,所以我写这个代码:

 // If we are unit testing, then... if (defined('UNIT_TESTING') && UNIT_TESTING) { // ...make our my_rand() function deterministic to aid testing. function my_rand($min, $max) { return $GLOBALS['random_table'][$min][$max]; } } else { // ...else make our my_rand() function truly random. function my_rand($min = 0, $max = PHP_INT_MAX) { if ($max === PHP_INT_MAX) { $max = getrandmax(); } return rand($min, $max); } } 

然后我设置random_table,因为我需要每个testing。

testing一个随机函数的真随机性是一个单独的testing。 我会避免在unit testing中testing随机性,而是会使用单独的testing和谷歌随机函数在您正在使用的编程语言中的真正的随机性。 非确定性testing(如果有的话)应该被排除在unit testing之外。 也许有一个单独的testing套件,这需要人力投入或更长的运行时间,以最大限度地减less失败的机会是真正的通过。

我不认为unit testing是为了这个。 你可以使用unit testing来返回一个随机值的函数,但是使用一个固定的种子,在这种情况下,它们不是随机的,可以这么说,对于随机种子,我不认为unit testing就是你想要的对于RNG,你的意思是一个系统testing,在这个系统testing中,你运行RNG很多次,看看它的分布或时刻。