25

私がモデルにメソッドを持っていた:モデルのコールバックメソッドを個別にテストする方法は?

class Article < ActiveRecord::Base 
    def do_something 
    end 
end 

私もこの方法のためのユニットテストを持っていた:私はそれがafter_saveコールバックには、このメソッドを移動する方が良いでしょう見つけるまで

# 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 

すべてが大丈夫でした:

class Article < ActiveRecord::Base 
    after_save :do_something 

    def do_something 
    end 
end 

この方法に関する私のテストはすべて壊れました。私はでそれを修正する必要があります:createsaveは同様に、このメソッドをトリガーするためdo_something

  • これ以上の具体的な呼び出し、または私は重複デシベルアクションを満たしています。代わりに、個々のメソッド呼び出し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 
    

build

  • テスト
  • respond_to使用することを
  • 変更create一般model.save渡されたテストが、私の懸念は、それが具体的な方法についてはもはやいません。さらにが追加された場合は、が他のコールバックと混同されます。

    私の質問は、モデルのインスタンスメソッドをテストするための美しい方法がありますか独立してはコールバックになりますか?

  • +0

    あなたの独創的なアプローチは、まだテストのために動作しない理由は明らかではありません。メソッドを直接テストし、それがコールバックとして個別に呼び出されることをテストします。私は何かを逃しているのですか、あなたはそのアプローチについて好きではない何かがありますか? –

    +0

    @AndrewHubbs、ご質問ありがとうございます。理由は、このメソッドがdbを変更したためです。たとえば、この記事はカテゴリ「Rails」に割り当てられます。コールバックにリファクタリングした後、FactoryGirl.createを呼び出すと、このコールバックが有効になり、記事がカテゴリ「Rails」に割り当てられます。テスト中にこのメソッドを再度呼び出すと、既に割り当てられているためエラーが発生します。 –

    答えて

    54

    コールバックとコールバックの動作は独立したテストです。 after_saveコールバックをチェックするには、次の2つのことを考える必要があります。

    1. 正しいイベントでコールバックが実行されていますか?
    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 
    

    。たとえば、user.before_save { user.article.do_something }のように、他の関連オブジェクトが更新されたときに同じコールバックメソッドarticle.do_somethingをトリガすることができます。これはすべてのものに対応します。

    したがって、いつものようにメソッドをテストしてください。コールバックについて別々に心配します。

    編集:タイプミスと潜在的な誤解 編集:変更は、「何かをトリガー」するために「何かをする」

    +0

    +1「行動からコールバックを切り離す」。 RDX、 "#実際のdo_somethingメソッドのテスト"の推奨事項は何ですか?私のメソッドを後で使用する(オブジェクトを "保存"することによって全体的な動作をテストする)? –

    +0

    'do_something'メソッドの実際のテストです。あなたの投稿の最初のrspecにいくらか似ています:' @ article.do_something; (@article).to have_something'を期待する。基本的に 'save'はより大きなメソッドです。これはbefore_savesやafter_savesなどの多くのトリガを引き起こす可能性があります。しかし、すべてのメソッドをテストし、すべてのメソッドが正しく機能していることがわかっている場合は、 save/before_save/after_save/...メソッドに対する非常に少ないテストと短いテスト。よくテストされた小さな関数から大きな関数を作成してください:) – Subhas

    +0

    偶然、あなたはhttp://stackoverflow.com/questions/35950470/rails-factorygirl-trait-association-with-model-after-create-callbackに関する考えを持っていますか? -not-setting –

    0

    私は私のテストでコールバックをスキップする方法を求めていました。これは、答えよりもコメントの詳細ですが、私は構文強調表示のためにここにそれを置く...

    、これは私がやったことです。 (これは壊れたテストに役立つかもしれません)。

    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 
    
    +0

    テディ、あなたの答え、またはコメントありがとう? :)私は恐れて、この方法で少し高いが、いくつかの余分なコードをテストし、モデルを意図的にテストに適応させるように変更することは恐れている。 –

    +0

    ええ、コールバックをスキップすることが必要なDBをシードしなければならなかったので、私の場合はもっと有益です。 – Teddy

    +0

    これにはskip_callbackメソッドがあります。https://github.com/rails/rails/blob/b894b7b90a6aced0e78ab84a45bf1c75c871bb2d/activesupport/lib/active_support/callbacks.rb#L620 –

    13

    あなたがそれらを呼び出すことなく、あなたのコールバックの存在をテストするために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 
    
    +0

    すばらしい編集@dennis!ありがとう:) –

    -1
    describe "#do_something" do 
    
    it "gives the article something" do 
    
        @article = FactoryGirl.build(:article) 
    
        expect(@article).to have_something 
    
    @article.save 
    end 
    
    end 
    
    関連する問題