我们如何对使用随机性的函数进行单元测试?

时间:2017-12-25 05:56:05

标签: unit-testing random

我对使用随机性的单元测试函数的最佳实践技术(如果有的话)感兴趣。要明确的是,我关注测试随机数生成器的分布。

作为一个玩具示例,让我们考虑一下这个功能:

// Returns a random element from @array. @array may not be empty.
int GetRandomElement(int[] array);

this question的答案表明,我们可能会注入随机性的模拟来源,这是有道理的。但我不确定如何使用模拟。例如,假设我们有这个接口:

// A mock-friendly source of randomness.
interface RandomnessSource {
  // Returns a random int between @min (inclusive) and @max (exclusive).
  int RandomInt(int min, int max);
}

...并将GetRandomElement()的签名更改为:

// Returns a random element from @array, chosen with @randomness_source.
// @array may not be empty.
int GetRandomElement(int[] array, RandomnessSource randomness_source);

好的,现在测试看起来像:

MockRandomnessSource mock = new MockRandomnessSource();
mock.ExpectCall(RandomnessSource::RandomInt(0, 5)).Return(2);
AssertEquals(GetRandomElement({0, 10, 20, 30, 40}, mock), 20);

...哪种方法可以正常工作,但只有实现如下:

// A fairly intuitive implementation.
int GetRandomElement(int[] array, RandomnessSource randomness_source) {
  // Pick a random number between [0..n), where @n is the @array's legnth.
  return array.Get(randomness_source.RandomInt(0, array.Length()));
}

...但是函数规范中没有任何内容阻止这样的实现:

// Less intuitive, but still a conforming implementation.
int GetRandomElement(int[] array, RandomnessSource randomness_source) {
  // Pick a random number between [1..n+1), only to subtract 1 from it.
  return array.Get(randomness_source.RandomInt(1, array.Length() + 1) - 1);
}

我想到的一个想法是,我们可能会进一步约束函数的契约,如下所示:

// Returns a random element from @array, chosen with @randomness_source by
// by calling @RandomnessSource::RandomInt to pick a random index between
// 0 and the length of @array.
int GetRandomElement(int[] array, RandomnessSource randomness_source);

...但我不能完全认为这对功能合同施加了太大的限制。

我还怀疑可能有更好的方法来定义接口RandomnessSource以使其调用者更适合单元测试,但我不太清楚是什么/如何。

......这让我想到了一个问题:使用随机性的单元测试函数有哪些最佳实践技巧(如果有的话)?

1 个答案:

答案 0 :(得分:0)

通常,您不想进行单元测试"随机性"。单元测试主要用于确定预期输入是否会为您提供预期输出。非常明确和结构化。您希望每次运行测试时,您的单元测试都是可预测的,并且行为方式相同。

如果你真的想测试它,问题就变成你想要测试它的具体程度如何?如果在两次测试之间结果不同会发生什么?如何定义成功的测试与失败的测试?

这些问题在正常的单元测试中更为明显,其中结果非常明确并且事先已经明确定义。