Rspec expect(instance) to receive method not working as expected - ruby-on-rails

#controller file
def update
#payment = Payment.find_by(reference_id: params[:reference_id])
if #payment.update(update_params)
#payment.do_something
end
end
when trying to spec if do_something method was called, by
expect(#payment).to receive(:do_something)
it says
expected: 1 time with any arguments
received: 0 times with any arguments
do_something is in my Payment Class. It is actually being called, but rspec says not.
Any ideas? thanks in advance

Firstly you need to stub the lines in controller in order to expect some code
before do
allow(Payment).to receive(:find_by).and_return(payment)
allow(payment).to receive(:update).and_return(true)
allow(payment).to receive(:do_something)
end
Also, instance variable in controller won't be directly accessible in rspecs.
So, First create a payment object in rspecs using let and use it before block like I did it in above solution

Your #payment in specs is actually a totally different variable, which is part of specs class, not the controller. I may be wrong, but that is my assumption from the parts of code you post - add specs code for more info.
As of the solution, may use 'stub any instance'
Payment.any_instance.stub(:do_something).and_return(:smthing)
A more complicated approach - using doubles

Related

How to test if method is called in RSpec but do not override the return value

There are already questions similar to this, but they all override the return values to nil unless .and_return is called as well
PROBLEM
I am wondering if there is a way to just check if a method is called using expect_any_instance_of(Object).to receive(:somemethod) and it runs normally without overriding or affecting the return value of .somemethod.
rspec-3.4.0
rails 4.2
Consider the following:
# rspec
it 'gets associated user' do
expect_any_instance_of(Post).to receive(:get_associated_user)
Manager.run_processes
end
# manager.rb
class Manager
def self.run_processes
associated_user = Category.first.posts.first.get_associated_user
associated_user.destroy!
end
end
The spec above although will work because :get_associated_user is called in the run_processes, however it raises NoMethodError: undefined method 'destroy!' for NilClass precisely because I mocked the :get_associated_user for any instance of Post.
I could add a .and_return method like expect_any_instance_of(Post).to receive(:get_associated_user).and_return(User.first) so that it will work without raising that error, but that already is a mocked return value (which might affect the rest of the code below it), and not the correct expected value it should have returned at the time the method is called.
I can however specify .and_return(correct_user) where correct_user is the user that is going to be the same return value as if it has ran normally. However, this will need me to mock every return value in the sequence Category.first.posts.first.get_associated_user just so that it will work normally. The actual problem is a lot more complex than above, therefore stubbing is not really a possible solution in my case.
You can use and_call_original on the fluent interface to "pass
through" the received message to the original method.
https://www.relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/calling-the-original-method
expect_any_instance_of(Post).to receive(:get_associated_user).and_call_original
However the use of expect_any_instance_of might be telling you that you have a code smell and you should be testing the behavior - not the implementation.
# test what it does - not how it does it.
it 'destroys the associated user' do
expect { Manager.run_processes }.to change(Category.first.posts.first.users, :count).by(-1)
end

how to correctly use stub for testing in rspec

So, I have a method in a class as follow:
def installation_backlog
Api::Dashboards::InstallationsBacklog.new(operational_district_id, officer_id).backlog_tasks
end
And I want to spec it. So, I just wrote an RSpec test to test it as follow:
it "should call a new instance of InstallationsBacklog with the backlog_tasks method" do
expect_any_instance_of(Api::Dashboards::InstallationsBacklog).to receive(:backlog_tasks)
#installation_officer.installation_backlog # #installation_officer is a new instance of the container class.
end
And this is working.
However, I began to wonder if this was a correct way of doing it. Like: am I sure that even if I stub the wrong ( probably inexistent ) method, and test for it, will it pass or fail?
I tried it, it passed
So, if later, the method name gets changed, there is no way for this test to detect that.
So, here is the question: How can I be sure that a RSpec stubbed method is actually existent in a code?
Here's how I'd set it up. Might help..
let(:backlog) {
double('Backlog', backlog_tasks: [])
}
before do
allow(Api::Dashboards::InstallationsBacklog).to receive(:new).
and_return(backlog)
end
it 'instantiates InstallationBacklog' do
expect(Api::Dashboards::InstallationBacklog).to receive(:new).
with(operational_district_id, officer_id)
#installation_officer.installation_backlog
end
it 'calls backlog_tasks on instance of InstallationBacklog' do
expect(backlog).to receive(:backlog_tasks)
#installation_officer.installation_backlog
end

Rspec is it possible to do something like should_receive(:foo).with(:bar).and_call_original.exacly(1).times?

Is it possible to do something like ServiceObject.should_receive(:foo).with(:bar).and_call_original.exacly(1).times?
My specs look something like this:
it 'should call instance of service object\'s :baz! method' do
ServiceObject.any_instance.should_receive(:baz!).exactly(1).times
end
it 'should call service object\'s :foo method' do
ServiceObject.should_receive(:foo).with(:bar).and_call_original.exacly(1).times
end
If I remove and_call_original the first spec fails. If I comment out the .exacly(1).times in the second spec both specs pass.
Two questions:
Why do these two specs interfere with one another?
Is there a way to call something that means .should_receive(:foo).with(:bar).and_call_original.exacly(1).times?
Thanks in advance!
and_call_original doesn't return the current example so it cannot work.
Instead you can do:
.should_receive(:foo).with(:bar).exacly(1).times.and_call_original

How do I use parameters in a constructor in a rails model

I trying to learn tdd using RSpec. I took this example from a cheat sheet I found online and am a bit confused as to how I would implement it. To add MovieList.new is automatic but how would I go about adding a parameter when it is already handled with ActiveRecord. And then to add the 'forward' method as well.
describe "forward" do
it "should jump to a next movie" do
next_movie = MovieList.new(2).forward
next_movie.track_number.should == 2
end
end
If this is a test for a MovieList class, create a class called MovieList.
Then in your constructor for that class, make sure it takes in a parameter called track_number, in your test that's the 2.
Then create a method called forward to do whatever you need it to do?
Here's a good example of where I'm going with this:
http://rspec.info/
This may sound ambiguous, but so was the question.
EDIT:
This is a rough idea of how to create a new MovieList class and initialize it with a parameter called track_number.
def MovieList
attr_accessor :track_number
def initialize(track_number)
#track_number = track_number
end
# You can define all your class methods below, you
# can start with forward.
def forward
# do something...
end
end
movie = Movie.new(:track_number => 2)
movie.forward
I am not sure what forward does in your example because you seem to be initializing track_number to 2 then calling forward. I would have expected track_number to increment but your test is checking to see if it's 2 still.
Note, I don't believe you need to change your constructor to take the parameter as long as you pass it in as a hash (the single member hash is implied in my example)...can someone verify or refute this last assertion?

Expect method call and proxy to original method with RSpec

I want to discover with BDD missing :include params for ActiveRecord::Base.find method. So my idea is to have in spec something like this:
ActiveRecord::Base.should_receive(:find).once.and_proxy_to_original_method
parent = SomeClass.find 34
parent.child.should be_loaded
parent.other_children.should be_loaded
If #child or #other_children associations are not eager loaded, expectation should fail with something like:
"Expected ActiveRecord::Base.find to be invoked once but it was invoked 2 more times with following args: 1. ...; 2. ..."
Does anyone know if there's some matcher that works like this or how to make this.
Thanks
I think I had the same problem here. In your particular case I would do this which I find quite clean.
original_method = ActiveRecord::Base.method(:find)
ActiveRecord::Base.should_receive(:find).once do (*args)
original_method.call(*args)
end
I believe you could extend the Rspec Mocks::MessageExpectation class to include the and_proxy_to_original_method method, shouldn't be too hard, but I haven't looked.

Resources