21

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

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

当我使用 RSpec 测试这个方法时,rand(4)它里面总是生成 0。我不是在测试randRails 的方法,我是在测试我的方法。测试我的方法的常见做法是什么?

4

5 回答 5

35

我会考虑两种方法:

方法1

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

before :each do
  srand(67809)
end

这适用于 Ruby 版本,并在您想要涵盖特定组合的情况下为您提供控制。我经常使用这种方法——仔细想想,那是因为我正在测试的代码rand()主要用作数据源,其次才(如果有的话)用于分支。它也被调用了很多,因此对返回值进行逐个调用控制会适得其反,我最终会铲入大量“看起来随机”的测试数据,可能首先通过调用来生成它rand()

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

方法二:

如果由于输出的值而具有分支点,并且您的断言属于“如果它选择 X,则 Y 应该发生”的类型,那么在同一个测试套件中模拟返回您想要的值的东西rand()也是合理的rand( n )断言:

 require 'mocha/setup'

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

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

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

于 2013-06-19T15:32:06.617 回答
15

我会提取随机数生成:

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

于 2013-06-19T15:55:56.673 回答
12

似乎最好的主意是使用 stub 而不是 real rand。这样你就可以测试你感兴趣的所有值。正如模块rand中定义的那样,Kernel你应该使用:

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

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

于 2013-06-19T15:44:02.463 回答
6

我发现这只是存根 rand ie。使用 Samuil 回答的 Kernel.stub(:rand) 最初不起作用。我要测试的代码直接称为 rand 例如

随机数 = 兰德

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

random_number = Kernel.rand

然后存根工作。

于 2013-12-18T02:57:05.770 回答
1

这适用于 RSpec:

allow_any_instance_of(Object).to receive(:rand).and_return(1)
于 2019-03-20T00:36:12.317 回答