RSpec在`rand`里面测试`rand`

时间:2013-06-30 13:52:22

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

我正在开发的游戏中有以下方法:

  def search
    if rand(5) == 0
      weapon = Weapon.offset(rand(Weapon.count)).first
      users_weapon = UsersWeapon.create(user_id: self.id, weapon_id: weapon.id, ammo: weapon.max_ammo)
      self.users_weapons << users_weapon
      { :success => 'true', :weapon => users_weapon }
    else
      { :success => 'false' }
    end
  end

如您所见,我在那里有两个rand 现在,当我尝试使用rspec测试它时,我想测试以下内容:

context 'when searches location' do
  subject(:user) { FactoryGirl.create :User }
  before { FactoryGirl.create :knife }
  before { FactoryGirl.create :pistol }

  context 'when finds knife' do
    before { srand(2) }
    it { user.search[:weapon].weapon.name.should == 'Knife' }
  end

  context 'when finds pistol' do
    before { srand(3) }
    it { p user.search[:weapon][:name].should == 'Pistol' }
  end

无论我在srand传递的是什么,它只能以两种方式之一起作用:
a)返回Pistol
OR
b)返回nil。

我想独立存根这些rand。我不想在每个上下文中只种一weapon因为我想实际测试随机武器选择。我怎么能表演呢?

2 个答案:

答案 0 :(得分:2)

使用意图揭示你的方法的名称,然后模拟该方法而不是rand

def search
  if rand(5) == 0
    weapon = Weapon.find_random
    # ...

class Weapon
  # ...
  def self.find_random
    self.offset(rand(self.count)).first

现在您可以轻松模拟Weapon.find_random

如果需要,可以在以后轻松更改您的实施

class Weapon
  # ...
  def self.find_random
    # postgres
    self.order("RANDOM()").limit(1).first
    # mysql
    self.order("RAND()").limit(1).first        

答案 1 :(得分:0)

几乎所有rand()都是伪随机生成器,它们的随机性取决于你启动它们的种子,停止播种相同的数字,而不是使用时间(srand(gettime()))或每次都改变的东西。

PS:gettime()应该返回一个int,你必须为Ruby做一个包装器,正常的时间函数返回它将它转换为int