RSpecで「any_instance」「should_receive」を何度でも言う方法
Railsに複数のレコードを持つ複数のcsvファイルをデータベースにインポートするインポートコントローラーがあります。レコードがRSpecを使用して実際に保存されているかどうかをRSpecでテストしたいです。
<Model>.any_instance.should_receive(:save).at_least(:once)
しかし、次のようなエラーが表示されます:
The message 'save' was received by <model instance> but has already been received by <another model instance>
コントローラーの不自然な例:
rows = CSV.parse(uploaded_file.tempfile, col_sep: "|")
ActiveRecord::Base.transaction do
rows.each do |row|
mutation = Mutation.new
row.each_with_index do |value, index|
Mutation.send("#{attribute_order[index]}=", value)
end
mutation.save
end
RSpecを使用してこれをテストすることは可能ですか、または回避策はありますか?
これには新しい構文があります:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
:newメソッドをオーバーライドする必要を回避するより良い答えは次のとおりです。
save_count = 0
<Model>.any_instance.stub(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
スタブメソッドは、制約なしの任意のインスタンスにアタッチできるようで、doブロックは、正しい回数と呼ばれることを確認するためにチェックできるカウントを作成できるようです。
更新-新しいrspecバージョンには次の構文が必要です。
save_count = 0
allow_any_instance_of(Model).to receive(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
私は最終的に自分に合ったテストを作成することができました:
mutation = FactoryGirl.build(:mutation)
Mutation.stub(:new).and_return(mutation)
mutation.should_receive(:save).at_least(:once)
スタブメソッドは、saveメソッドを複数回受信する単一のインスタンスを返します。単一のインスタンスなので、any_instance
メソッドとat_least
メソッドは通常。
このようなスタブ
User.stub(:save) # Could be any class method in any class
User.any_instance.stub(:save) { |*args| User.save(*args) }
次に、このように期待します:
# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)
これは、 このGist の簡略化であり、any_instance
、元のメソッドにプロキシする必要がないため。他の用途については、そのGistを参照してください。
これはRSpec 3.3を使用したRobの例であり、Foo.any_instance
をサポートしなくなりました。これは、ループ内でオブジェクトを作成するときに役立ちます
# code (simplified version)
array_of_hashes.each { |hash| Model.new(hash).write! }
# spec
it "calls write! for each instance of Model" do
call_count = 0
allow_any_instance_of(Model).to receive(:write!) { call_count += 1 }
response.process # run the test
expect(call_count).to eq(2)
end
私の場合は少し違っていましたが、私もこの質問に答えて、答えをここに落とすことにしました。私の場合、特定のクラスのインスタンスをスタブ化したかったのです。 expect_any_instance_of(Model).to
を使用したときに同じエラーが発生しました。 allow_any_instance_of(Model).to
に変更すると、問題は解決しました。
さらなる背景についてはドキュメントをご覧ください: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
クラスのnew
の数を数えてみてください。実際にはsave
sの数をテストするわけではありませんが、十分かもしれません
expect(Mutation).to receive(:new).at_least(:once)
それが何回保存されたかについての唯一の期待がある場合。 Harm de Wit
自身の答えのように、おそらく完全に機能するファクトリの代わりにspy()
を使用したいでしょう。
allow(Mutation).to receive(:new).and_return(spy)
...
expect(Mutation.new).to have_received(:save).at_least(:once)