Stubbing Chained Queries in Rails 3 and Rspec
I'm trying to test a scope I have that is based upon a chain of other scopes. ("public_stream" below).
scope :public, where("entries.privacy = 'public'")
scope :completed, where("entries.observation <> '' AND entries.application <> ''")
scope :without_user, lambda { |user| where("entries.user_id <> ?", user.id) }
scope :public_stream, lambda { |user| public.completed.without_user(user).limit(15) }
Using a test like this:
it "should use the public, without_user, completed, and limit scopes" do
@chain = mock(ActiveRecord::Relation)
Entry.should_receive(:public).and_return(@chain)
@chain.should_receive(:without_user).with(@user).and_return(@chain)
@chain.should_receive(:completed).and_return(@chain)
@chain.should_receive(:limit).with(15).and_return(Factory(:entry))
Entry.public_stream(@user)
end
However, I continue to receive this error:
Failure/Error: Entry.public_stream(@user)
undefined method `includes_values' for #<Entry:0xd7b7c0>
It seems includes_values is an instance v开发者_开发问答ariable of the ActiveRecord::Relation object, but when I try to stub it, I still receive the same error. I was wondering if anyone had experience with stubing Rails 3's new chained queries? I can find a bunch of discussion over 2.x's find hash, but nothing on how to test what's current.
I use rspec's stub_chain
for this. You might be able to use something like:
some_model.rb
scope :uninteresting, :conditions => ["category = 'bad'"],
:order => "created_at DESC"
Controller
@some_models = SomeModel.uninteresting.where(:something_else => true)
spec
SomeModel.stub_chain(:uninteresting, :where) {mock_some_model}
Same Answer as above.
SomeModel.stub_chain(:uninteresting, :where) {mock_some_model}
Rspec 3 version:
allow(SomeModel).to receive_message_chain(:uninteresting).and_return(SomeModel.where(nil))
Reference:
https://relishapp.com/rspec/rspec-mocks/docs/method-stubs/stub-a-chain-of-methods
First off, you probably should not be testing built-in Rails functionality.
You should only be writing unit tests for code that you have written yourself (which should be second-nature if you practice TDD) – Rails ships with its own comprehensive suite of unit tests for its built-functionality – there is no point in replicating this.
As far as the error being thrown, I think that your issue is on this line:
@chain.should_receive(:limit).with(15).and_return(Factory(:entry))
You are expecting the chain to return a Factory
, which would effectively be an instance of ActiveRecord
, but in actuality every relation returns yet another ActiveRecord::Relation
.
Thus, your expectation itself is incorrect, and may indeed be causing the error that's being thrown.
Keep in mind, that scopes don't actually return the records you expect, until you explicitly iterate over them. Also, the relation's records will never return a single record. They will always either return an empty array or an array with records.
Try passing on the Arel, as it might be the case that it scopes are going missing.
it "should use the public, without_user, completed, and limit scopes" do
@chain = Entry
@chain.should_receive(:public).and_return(@chain.public)
@chain.should_receive(:without_user).with(@user).and_return(@chain.without_user(@user))
@chain.should_receive(:completed).and_return(@chain.completed)
@chain.should_receive(:limit).with(15).and_return(Factory(:entry))
Entry.public_stream(@user)
end
精彩评论