Stubbing Chained Methods with Rspec
I figured something out.
Client.stub!(:named_scope).and_return(@clients = mock([Client]))
@clients.stub!(:first).and_return(@client = mock(Client))
which allows me to call my controller:
@client = Client.named_scope(param).first
It works, but is there a better solution?
EDIT:
The release of rspec 1.2.6 allows us to use stub_chain meaning it can now be:
Client.stub_chain(:named_scope, :chained_call).and_return(@clients = [mock(Client)])
This was top of my head, as always check the api for specifics :)
How do I 'expect' a chain of methods using Rspec where the first method takes a parameter?
Generally speaking, I prefer not to use stub chains, as they are often a sign that you are violating the Law of Demeter. But, if I had to, this is how I would mock that sequence:
let(:vanity_url) { 'https://vanity.url' }
let(:partner_campaigns) { double('partner_campaigns') }
let(:loaded_partner_campaigns) { double('loaded_partner_campaigns') }
let(:partner_campaign) do
double("Contentful::Model", fields {:promotion_type => "Promotion 1"}
end
before do
allow(Contentful::PartnerCampaign)
.to receive(:find_by)
.with(vanity_url: vanity_url)
.and_return(partner_campaigns)
allow(partner_campaigns)
.to receive(:load)
.and_return(loaded_partner_campaigns)
allow(loaded_partner_campaigns)
.to receive(:first)
.and_return(partner_campaign)
end
How to stub two chained ActiveRecord methods in RSpec?
RSpec gives you two ways to stub chained method calls.
The succinct way is receive_message_chain
:
allow(Green::Trees).to receive_message_chain(:where, :find_each).and_yield(tree)
That doesn't let you specify arguments, however, although it often isn't important to do so.
If you care about arguments, you can do it like this:
results = double
allow(Green::Tree).to receive(:where).with(id: ids).and_return(results)
allow(results).to receive(:find_each).and_yield(tree)
What you wrote would work, but it's incorrect since where
doesn't return the class Green::Tree
, but an ActiveRecord relation. Green::Tree
does implement find_each
, but it's a different method with the same name as the one on the relation. Very confusing!
RSpec: Stub chains with arguments?
You can use this:
Payment.stub_chain(:order, :where).with(:updated_at).with(:paid => true) { return_this }
Stubbing Chained Queries in Rails 3 and Rspec
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}
How to test chained methods in Ruby on Rails using Rspec
Coming back to this 3 years later. I would would approach it entirely differently.
The benefit of the code below is that in order to write tests for InterviewGrader I would no longer need to worry about how the scores are attained.
I just give it the scores and test it gives me the correct output.
Also I would never need to worry about the underlying implementation of InterviewGrader. However, if the logic was changed at a later date, the tests would fail.
The new scores
method on User
would need to be tested separately.
class InterviewGrader
def self.run scores
new(scores).run
end
attr_reader :scores
def initialize(scores)
@scores = scores
end
def run
scores.inject { |sum, score|
sum + score
}.to_f / number_of_scores
end
private
def number_of_scores
scores.length
end
end
class User
has_many :interviews
def scores
interviews.map(&:score)
end
def interview_grade
InterviewGrader.run(scores)
end
end
class Interview
belongs_to :user
end
Related Topics
Safe Navigation Equivalent to Rails Try for Hashes
How to Store Enum as String to Database in Rails
Set Default Stage with Capistrano 3
Rspec: How to Write a Test That Expects Certain Output But Doesn't Care About the Method
How to Embed Ruby in JavaScript (Rails + .Html.Erb File)
How to Access the Base Namespace in Ruby
Examples of 'Things' That Are Not Objects in Ruby
Ruby Gem Listed, But Won't Load (Gem in User Dir, Not Ruby Dir)
List Dynamic Attributes in a Mongoid Model
What Is the Point of Object#Presence in Rails
How to See the Ruby Code in a Proc
Perform One Validation Only If All Other Validations Pass
Using Will_Paginate Without :Total_Entries to Improve a Lengthy Query
Enumerator as an Infinite Generator in Ruby
Testing Simple Sti with Factorygirl
Sequel Accessing Many_To_Many Join Table When Adding Association