モデルにメソッドがありました:
class Article < ActiveRecord::Base
def do_something
end
end
このメソッドの単体テストもありました:
# spec/models/article_spec.rb
describe "#do_something" do
@article = FactoryGirl.create(:article)
it "should work as expected" do
@article.do_something
expect(@article).to have_something
end
# ...several other examples for different cases
end
このメソッドをafter_save
コールバックに移動する方が良いとわかるまで、すべてがうまくいきました。
class Article < ActiveRecord::Base
after_save :do_something
def do_something
end
end
これで、このメソッドに関するすべてのテストが壊れました。私はそれを修正しなければなりません:
create
またはsave
もこのメソッドをトリガーするため、do_something
への具体的な呼び出しはありません。そうでない場合は、dbアクションが重複します。create
をbuild
に変更します個々のメソッド呼び出しmodel.save
の代わりに一般的なmodel.do_something
を使用します
describe "#do_something" do
@article = FactoryGirl.build(:article)
it "should work as expected" do
expect{@article.save}.not_to raise_error
expect(@article).to have_something
expect(@article).to respond_to(:do_something)
end
end
テストは合格しましたが、私の懸念は、特定のメソッドに関するものではなくなったことです。効果は追加される場合は他のコールバックと混合されますになります。
私の質問は、モデルのインスタンスメソッドをテストする美しい方法はありますか独立してコールバックになることですか?
コールバックとコールバックの動作は独立したテストです。 after_saveコールバックを確認する場合は、次の2つのことを考える必要があります。
Article
クラスに多くのコールバックがあると仮定します。これがテスト方法です。
class Article < ActiveRecord::Base
after_save :do_something
after_destroy :do_something_else
...
end
it "triggers do_something on save" do
expect(@article).to receive(:do_something)
@article.save
end
it "triggers do_something_else on destroy" do
expect(@article).to receive(:do_something_else)
@article.destroy
end
it "#do_something should work as expected" do
# Actual tests for do_something method
end
これにより、コールバックが動作から切り離されます。たとえば、article.do_something
など、他の関連オブジェクトが更新されたときに同じコールバックメソッドuser.before_save { user.article.do_something }
をトリガーできます。これにより、これらすべてが収容されます。
そのため、通常どおりメソッドをテストし続けます。コールバックを個別に心配します。
編集:タイプミスと潜在的な誤解編集:「何かをする」を「何かをトリガーする」に変更する
shoulda-callback-matchers を使用して、コールバックを呼び出さずにテストできます。
describe Article do
it { should callback(:do_something).after(:save) }
end
コールバックの動作もテストする場合:
describe Article do
...
describe "#do_something" do
it "gives the article something" do
@article.save
expect(@article).to have_something
end
end
end
Sandi Metzとミニマリスト テスト の精神で、 https://stackoverflow.com/a/16678194/2001785 の提案はおそらくプライベートメソッドへの呼び出しを確認するために私には正しくないようです。
公に観察可能な副作用をテストするか、発信コマンドメッセージを確認することは、私にとってより理にかなっています。 Christian Rolleは http://www.chrisrolle.com/en/blog/activerecord-callback-tests-with-rspec で例を提供しました。
これは答えというよりもコメントですが、構文を強調するためにここに配置します...
私はテストでコールバックをスキップする方法を望んでいました、これは私がやったことです。 (これは、壊れたテストに役立つ可能性があります。)
class Article < ActiveRecord::Base
attr_accessor :save_without_callbacks
after_save :do_something
def do_something_in_db
unless self.save_without_callbacks
# do something here
end
end
end
# spec/models/article_spec.rb
describe Article do
context "after_save callback" do
[true,false].each do |save_without_callbacks|
context "with#{save_without_callbacks ? 'out' : nil} callbacks" do
let(:article) do
a = FactoryGirl.build(:article)
a.save_without_callbacks = save_without_callbacks
end
it do
if save_without_callbacks
# do something in db
else
# don't do something in db
end
end
end
end
end
end