Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 335
Jay-Ar Polidario Avatar asked Feb 17 '16 11:02

Jay-Ar Polidario


1 Answers

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
like image 185
max Avatar answered Sep 24 '22 17:09

max