单元测试 - 如何测试返回随机输出的函数?

时间:2010-04-11 17:53:07

标签: unit-testing

我有一个函数,它接受两个参数,并返回一个或另外50%的时间。

对此的单元测试应确定可以返回两个参数。幸运的是,我不需要证明每个的概率是50%,但我需要证明两个参数都可以返回。

如何为此功能编写测试用例?

6 个答案:

答案 0 :(得分:21)

如果随机性基于它调用的随机数生成器,您可以设置一个stub random()函数,该函数返回各种结果,以便为您提供预期的输出。

答案 1 :(得分:4)

你可以做以下两件事之一:

  1. 创建一个stub random()生成器,它可以在单元测试时返回一致的数字。
  2. 运行您的代码N次,其中N足够高,只要代码正常工作,您就可以获得一致的结果。
  3. 选项#1几乎总是首选。通过包含随机数生成器,您不再测试原子单元代码,并通过将随机组件包含到单元测试中,您需要进行统计以验证代码是否正常工作。在合理数量的测试中,选项#2永远不会出现一些错误,这令人担忧。

答案 2 :(得分:3)

不确定这是否是最佳方法,但我会使用while循环,其中包含我根据函数获取的值设置的两个标志。我还会使用某种限制,以便在测试失败时测试不会进入无限循环(即,没有收到两个值中的一个)。所以:

int limit = 100;
int i = 0;
while(!firstValueReceived && !secondValueReceived && i < limit) {
   int value = randomFunc();

   if(!firstValueReceived) {
      firstValueReceived = (value == firstValue);
   }

   if(!secondValueReceived) {
      secondValueReceived = (value == secondValue);
   }
   i++;
}

assertTrue(firstValueReceived && secondValueReceived);

我猜你可以使用测试悬挂作为指标来决定它是否失败,所以你可以取消限制:

while(!firstValueReceived && !secondValueReceived) {
   int value = randomFunc();

   if(!firstValueReceived) {
      firstValueReceived = (value == firstValue);
   }

   if(!secondValueReceived) {
      secondValueReceived = (value == secondValue);
   }
}

assertTrue(firstValueReceived && secondValueReceived);

答案 3 :(得分:3)

单位测试结果不是给定响应的单个值,而是一系列输入的值分布。概率意味着您必须运行许多测试来构建它。

如果你的对象真的应该平均返回一个值的一半,而剩下的时间则返回另一个值,那么适当的测试将是运行许多测试并确保限制行为为你提供所需的分发。建立适当的结果分布,并确保它符合您的期望。

答案 4 :(得分:2)

如果决定是真正随机的(或伪随机的),那么你可以从你的功能中分离出从另一个中选择一个结果的关注。例如,您的函数可以接受委托或其他对象作为参数,其唯一的工作是“翻转硬币”但不关心这两个参数或任何关于它们的逻辑。然后你的单元测试可以提供一个模拟决策者对象,它返回测试想要检查的结果。

答案 5 :(得分:1)

您通过分离和/或injection

进行测试
// ** WARNING: Untested code

<强>原始

void funcToTest(String a, String b) {
  int i = new Random().nextInt(2);
  return ((i == 0) ? a : b);
}

<强>分离

void funcToTest(String a, String b) {
  return funcToTest(a, b, (new Random().nextInt(2) == 0));
}
void funcToTest(String a, String b, boolean r) {
  return (r ? a : b);
}

现在测试

funcToTest(String a, String b, boolean r).

您相信,如果您测试上述内容,那么

funcToTest(String a, String b)

只会工作。

<强>喷吹

interface BoolRandomizer {
  bool getBool();
}
BoolRandomizer BOOL_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return (new Random().nextInt(2) == 0); }
}
BoolRandomizer ALWAYS_FALSE_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return false; }
}
BoolRandomizer ALWAYS_TRUE_RANDOMIZER = new BoolRandomizer() {
  public bool getBool() { return true; }
}

class ClassUnderTest {
    private final BoolRandomizer br;
    ClassUnderTest() { this(BOOL_RANDOMIZER); }
    ClassUnderTest(BoolRandomizer br) { this.br = br; }
    void funcToTest(String a, String b) {
      return (br.getBool() ? a : b);
    }
}

现在测试

new ClassUnderTest(ALWAYS_FALSE_RANDOMIZER).funcToTest()

new ClassUnderTest(ALWAYS_TRUE_RANDOMIZER).funcToTest()

当然有更复杂/更光滑的方法,但这是基本的想法。我使用这些技术进行一副卡片程序来测试随机播放功能。另一种情况:通过调用系统API gettime()来使用当前时间的函数可能很难测试,除非您愿意在非常特定的时间运行单元测试:-)。分离和注射易于测试。