如何在 Ruby on Rails 应用程序中使用 rSpec 测试观察者?

How would you test observers with rSpec in a Ruby on Rails application?

提问人:Nicolai Reuschling 提问时间:8/29/2008 最后编辑:Nicolai Reuschling 更新时间:8/28/2015 访问量:13148

问:

假设你的一个 Ruby on Rails 应用程序中有一个 ActiveRecord::Observer - 你如何使用 rSpec 测试这个观察器?

Ruby-on-Rails Ruby ActiveRecord rSpec 观察者模式

评论


答:

15赞 Orion Edwards 8/29/2008 #1

免责声明:我从未在生产站点上实际这样做过,但看起来一种合理的方法是使用模拟对象和朋友,并直接在观察者上调用方法should_receive

给定以下模型和观察者:

class Person < ActiveRecord::Base
  def set_status( new_status )
    # do whatever
  end
end

class PersonObserver < ActiveRecord::Observer
  def after_save(person)
    person.set_status("aha!")
  end
end

我会写一个这样的规范(我运行了它,它通过了)

describe PersonObserver do
  before :each do
    @person = stub_model(Person)
    @observer = PersonObserver.instance
  end

  it "should invoke after_save on the observed object" do
    @person.should_receive(:set_status).with("aha!")
    @observer.after_save(@person)
  end
end
35赞 Pete 9/25/2008 #2

您走在正确的轨道上,但是在使用 rSpec、观察器和模拟对象时,我遇到了许多令人沮丧的意外消息错误。当我对模型进行规范测试时,我不想在我的消息期望中处理观察者行为。

在您的示例中,在不知道观察者将要对模型执行的操作的情况下,没有一种真正好的方法可以在模型上指定“set_status”。

因此,我喜欢使用“No Peeping Toms”插件。鉴于您上面的代码并使用 No Peeping Toms 插件,我会像这样指定模型:

describe Person do 
  it "should set status correctly" do 
    @p = Person.new(:status => "foo")
    @p.set_status("bar")
    @p.save
    @p.status.should eql("bar")
  end
end

您可以指定您的模型代码,而不必担心有一个观察者会进来破坏您的价值。您可以在person_observer_spec中单独指定,如下所示:

describe PersonObserver do
  it "should clobber the status field" do 
    @p = mock_model(Person, :status => "foo")
    @obs = PersonObserver.instance
    @p.should_receive(:set_status).with("aha!")
    @obs.after_save
  end
end 

如果你真的真的想测试耦合的 Model 和 Observer 类,你可以这样做:

describe Person do 
  it "should register a status change with the person observer turned on" do
    Person.with_observers(:person_observer) do
      lambda { @p = Person.new; @p.save }.should change(@p, :status).to("aha!)
    end
  end
end

在99%的情况下,我宁愿在关闭观察者的情况下进行规范测试。这样就更容易了。

评论

0赞 roo 1/21/2013
如果你想测试观察者,那么我使用的模式是:这为PersonObserver描述块内的所有测试启用了观察者。describe PersonObserver { around(:each) { |spec| Person.with_observers(:person_observer) { spec.run } } }
0赞 Grant Birchmeier 5/2/2014
这个答案的措辞类似于回复,但不清楚是什么。它当然不是直接回答这个问题......
4赞 William Dix 4/6/2011 #3

no_peeping_toms现在是一颗宝石,可以在这里找到: https://github.com/patmaddox/no-peeping-toms

评论

0赞 Robert Ross 4/7/2012
从 Rails 3.1 开始,有一个针对观察者的禁用方法。
2赞 Sujoy Gupta 5/9/2011 #4

如果要测试观察者是否观察到正确的模型并按预期接收通知,下面是一个使用 RR 的示例。

your_model.rb:

class YourModel < ActiveRecord::Base
    ...
end

your_model_observer.rb:

class YourModelObserver < ActiveRecord::Observer
    def after_create
        ...
    end

    def custom_notification
        ...
    end
end

your_model_observer_spec.rb:

before do
    @observer = YourModelObserver.instance
    @model = YourModel.new
end

it "acts on the after_create notification"
    mock(@observer).after_create(@model)
    @model.save!
end

it "acts on the custom notification"
    mock(@observer).custom_notification(@model)
    @model.send(:notify, :custom_notification)
end