Rspec应该在一个不行为的实例上存在

时间:2012-09-13 06:25:32

标签: ruby-on-rails ruby testing rspec

我有一个类方法,只需遍历一些记录并在每条记录上执行一个实例方法。我正在尝试为类方法编写一个简单的测试,以确保它将实例方法发送到每个记录。

it "fires any pending alarms" do
  pending       = create :alarm, :pending
  other_pending = create :alarm, :pending

  # sanity check
  Alarm.pending.sort.should eq [pending, other_pending].sort

  pending.should_receive(:fire)
  other_pending.should_receive(:fire)

  Alarm.fire_pending
end

alarm.rb

class Alarm < ActiveRecord::Base
  scope :pending, where(pending: true)

  def self.fire_pending
    self.pending.each do |alarm|
      alarm.fire
    end
  end
end

但我收到should_receive期望值的错误:

 Failure/Error: pending.should_receive(:fire)
   (#<Alarm:0x007fcc96334310>).fire(any args)
       expected: 1 time
       received: 0 times

我成功地使用了这个期望。我担心我会遗漏一些明显的东西。

修改

我现在已经解决了这个问题:

it "fires any pending alarms" do
  pending       = create :alarm, :pending
  other_pending = create :alarm, :pending

  pending_alarms = Alarm.pending
  pending_alarms.sort.should eq [pending, other_pending].sort

  Alarm.stub(:pending) { pending_alarms }
  pending_alarms.first.should_receive(:fire)
  pending_alarms.last.should_receive(:fire)

  Alarm.fire_pending
end

但我真的不喜欢这样,并且想知道为什么我的第一次尝试不起作用。

1 个答案:

答案 0 :(得分:6)

Alarm.pending返回的对象与示例中创建的对象不同。你可以通过改变:

来看到这一点
pending_alarms.sort.should eq [pending, other_pending].sort

为:

# the eq matcher uses == (object equivalence)
# the equal matcher uses equals? (object identity)
pending_alarms.sort.tap do |sorted|
  sorted[0].should eq pending       # passes
  sorted[1].should eq other_pending # passes
  sorted[0].should equal pending       # fails
  sorted[1].should equal other_pending # fails
end

这是使用(邪恶的)any_instance

的好例子
it "fires any pending alarms" do
  pending = create :alarm, :pending
  Pending.any_instance.should_receive(:fire)
  Alarm.fire_pending
end