Ruby on Rails アプリケーションの 1 つに ActiveRecord::Observer があるとします。このオブザーバーを rSpec でどのようにテストしますか?
4 に答える
あなたは正しい道を進んでいますが、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% の確率で、オブザーバーをオフにして仕様テストを行います。その方が簡単です。
免責事項: 実稼働サイトで実際にこれを行ったことはありませんが、モック オブジェクト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
no_peeping_tomsはgemになり、ここで見つけることができます:https ://github.com/patmaddox/no-peeping-toms
オブザーバーが正しいモデルを監視し、期待どおりに通知を受信することをテストする場合は、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