代码之家  ›  专栏  ›  技术社区  ›  Extrakun

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

  •  13
  • Extrakun  · 技术社区  · 14 年前

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

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

    如何为这个函数编写测试用例?

    6 回复  |  直到 14 年前
        1
  •  20
  •   John    14 年前

    如果随机性是基于它调用的随机数生成器的,那么可以装配一个stub random()函数,该函数返回各种结果,以提供预期的输出。

        2
  •  4
  •   James Thompson    14 年前

    你可以做两件事之一:

    1. 生成一个stub random()生成器,它可以在单元测试时返回一致的数字。
    2. 运行代码n次,其中n足够高,只要代码正常工作,就可以得到一致的结果。

    选项1几乎总是首选。通过包含随机数生成器,您将不再测试代码的原子单元,并且通过将随机组件包含到单元测试中,您需要进行统计以验证代码是否正常工作。有一些错误是选项2在合理数量的测试中永远无法发现的,这是令人担忧的。

        3
  •  3
  •   Vivin Paliath    14 年前

    不确定这是不是最好的方法,但我会用 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);
    
        4
  •  3
  •   duffymo    14 年前

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

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

        5
  •  2
  •   Richard Walters    14 年前

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

        6
  •  1
  •   Community datashaman    7 年前

    通过分离和/或 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()来使用当前时间的函数可能很难测试,除非您愿意在一天中非常特定的时间运行单元测试:-)。分离和注射易测试。