Stubbing Chained Methods with Rspec

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



Leave a reply



Submit