2013-06-19 49 views
18

我有一个做了这样的方法:如何使用RSpec测试`rand()`?

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

当我使用RSpec的测试这种方法,rand(4)里面总是产生0。我不是测试的Rails rand方法,我测试我的方法。 测试我的方法的常用做法是什么?

回答

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

“黑盒子”测试要困难得多 - 您需要断言行为在统计学上没问题,而且您还需要接受非常广泛的可能性,因为有效的随机行为可能会导致幻像测试失败。

+2

另请参阅http://stackoverflow.com/questions/6077725/ruby-using-rand-in-code-but-writing-tests-to-verify-probabilities –

12

看来最好的想法是使用存根,而不是真正rand。这样你就可以测试你有兴趣在所有的值作为randKernel模块定义,你应该使用存根它:

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

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

+0

我认为这是一个不错的解决方案。我建议一个小改进:'Kernel.stub(:rand).with(任何){randomized_value}' –

+0

谢谢!忘记可选参数。 – samuil

+1

或者如果有多个代码路径并且每个都需要测试,暂时将'rand'转换为枚举数。 –

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的实施细则,如果你决定使用另一个随机数字发生器,您的测试不会中断。

5

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

RANDOM_NUMBER = RAND

然而,如果我改变的代码

RANDOM_NUMBER = Kernel.rand

然后根部工作。