Ruby on rails 如何使用RSpec测试'rand()'?

Ruby on rails 如何使用RSpec测试'rand()'?,ruby-on-rails,ruby,unit-testing,rspec,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方法,而是在测试我的方法。 测试我的方法的常见做法是什么?我会考虑两种方法: 方法1: 在srand(seed)中使用一个已知的see

我有一个方法,它可以做这样的事情:

def some_method
  chance = rand(4)
  if chance == 1 do
    # logic here
  else
    # another logic here
  end
end
当我使用RSpec测试这个方法时,它内部的
rand(4)
总是生成0。我不是在测试Rails的
rand
方法,而是在测试我的方法。
测试我的方法的常见做法是什么?

我会考虑两种方法:

方法1

srand(seed)
中使用一个已知的seed值,在
之前:每个
块:

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()


“黑盒”测试要困难得多——您需要断言行为在统计上是正常的,并且您还需要接受非常广泛的可能性,因为有效的随机行为可能会导致虚拟测试失败。

似乎最好的办法是使用存根,而不是真正的
兰德
。这样,您就可以测试所有感兴趣的值。由于
rand
是在
Kernel
模块中定义的,您应该使用以下方法对其进行存根:

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

在特定的上下文中,您可以使用
let
方法定义
随机化\u值

我将提取随机数生成:

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
的实现细节联系起来,而且如果您决定使用另一个随机数生成器,您的测试也不会中断。

我发现,仅使用Samuil回答的Kernel.stub(:rand)来stub rand最初不起作用。我要测试的代码直接调用rand,例如

随机数=兰德

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

随机数=Kernel.rand

然后存根工作。

这在RSpec中工作:

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

我认为这是一个很好的解决方案。我建议做一点小小的改进:
Kernel.stub(:rand).with(anything){randomized_value}
谢谢!忘记了可选参数。或者,如果有多个代码路径且每个路径都需要测试,则暂时将
rand
转换为枚举器。@这个解决方案的优点在于,将哪个随机值映射到哪个执行路径应该是实现细节。使用新的rspec语法,这相当于
allow(内核)。接收(:rand)。并返回(0.5)