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
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 :)
RSpec: Stub chains with arguments?
You can use this:
Payment.stub_chain(:order, :where).with(:updated_at).with(:paid => true) { return_this }
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: how to chain receive().with()?
IMHO you have to go the long way. There is no shorter way to describe it.
Regardless of that, I would recommend you overthink your testing strategy. At the moment you test that a very specific combination of methods is called but not if these method calls are actually doing what you want them to do.
Instead, I would create an example record that should be deleted (and perhaps a couple that should not be deleted), then run the job and afterward test that only the expected record was deleted.
For example like this:
let!(:record_to_be_deleted) {
Delayed::Job.create!(queue: 'produceable', locked_at: nil)
}
let!(:records_to_stay) do
[
Delayed::Job.create!(queue: 'produceable', locked_at: Time.current),
Delayed::Job.create!(queue: 'default', locked_at: nil)
]
end
it "should remove only expected records" do
expect {
instance.run_produceable_job
}.to chance { Delayed::Job.count }.from(3).to(2)
expect {
record_to_be_deleted.reload
}.to raise_error(ActiveRecord::RecordNotFound)
end
The rule of thumb is to test the expected outcome, not the specific implementation. Because the implementation might change, will be refactored or might break in future versions.
Related Topics
Gem Install Dm-Postgres-Adapter Build Error
Using Each_With_Index with Map
Undefined Method When Accessing Hash Element
Heroku and @Font-Face - Embedded Fonts Wont Display on Heroku
How to Use Your Own MySQL Database Server with Heroku
Ruby/Rails Collection to Collection
Application.CSS Not Being Served as an Asset
What's the Efficient Way to Multiply Two Arrays and Get Sum of Multiplied Values in Ruby
What Are Some Examples of Using Nokogiri
How to Read from Redis Inside a Multi Block in Ruby
How to Refer a Local Gem in Ruby
Should Rbenv Be Installed System-Wide, or at a User Level
"The Ruby Way" (Mixins and Class Reopening) VS. Dependency Injection
Ruby Method, Proc, and Block Confusion
Download File from S3 to Rails 4 App