如何用RSpec测试`rand()`?

时间:2013-06-19 15:28:42

标签: ruby-on-rails ruby unit-testing rspec

我有一个类似这样的方法:

def some_method
  chance = rand(4)
  if chance == 1 do
    # logic here
  else
    # another logic here
  end
end

当我使用RSpec测试此方法时,其中的rand(4)始终生成0.我没有测试Rails的rand方法,我正在测试我的方法。 测试我的方法的常见做法是什么?

5 个答案:

答案 0 :(得分:32)

我会考虑两种方法:

方法1

srand( seed )块的before :each中使用已知的种子值:

before :each do
  srand(67809)
end

这适用于Ruby版本,并在您想要涵盖特定组合的情况下为您提供控制。我经常使用这种方法 - 考虑它,因为我测试的代码主要使用rand()作为数据源,而仅使用其次(如果有的话)进行分支。它也被大量调用,因此对返回值进行逐个调用控制会适得其反,我最终会铲掉大量的测试数据,这些数据看起来是随机的,可能会在首先打电话给rand()

您可能希望在至少一个测试场景中多次调用您的方法,以确保您有合理的组合覆盖率。

方法2:

如果由于rand()的值输出而有分支点,并且您的断言属于"如果它选择X,那么Y应该发生",那么它也是合理的测试套件模拟出rand( n )的东西,它返回你想要做出断言的值:

 require 'mocha/setup'

 Kernel.expects(:rand).with(4).returns(1)
 # Now run your test of specific branch

本质上这些都是"白盒"测试方法,它们都要求您知道您的例程在内部使用rand()

A"黑匣子"测试要困难得多 - 您需要断言行为在统计上是正常的,并且您还需要接受各种各样的可能性,因为有效的随机行为可能会导致幻像测试失败。

答案 1 :(得分:13)

我将提取随机数生成:

def chance
  rand(4)
end

def some_method
  if chance == 1 do
    # logic here
  else
    # another logic here
  end
end

并存根:

your_instance.stub(:chance) { 1 }

这不会将您的测试与rand的实现细节联系起来,如果您决定使用其他随机数生成器,则您的测试不会中断。

答案 2 :(得分:12)

似乎最好的想法是使用存根,而不是真正的rand。通过这种方式,您可以测试您感兴趣的所有值。由于rand模块中定义了Kernel,您应该使用它来存根:

Kernel.stub(:rand).with(anything) { randomized_value }

在特定情况下,您可以使用randomized_value方法定义let

答案 3 :(得分:6)

我发现只是捣乱兰德即。使用Kernel.stub(:rand)作为Samuil的回答并没有最初的工作。我要测试的代码直接称为rand,例如

  

random_number = rand

但是,如果我将代码更改为

  

random_number = Kernel.rand

然后茬工作了。

答案 4 :(得分:1)

这在RSpec中有效:

allow_any_instance_of(Object).to receive(:rand).and_return(1)