1
votes

New to RSpec and having trouble stubbing out an update method (needs to be stubbed) and am running into this error. I have tried a bunch of different ways and can't seem to solve it. I tried to include only the necessary code.

/spec/controllers/admin/business_controller_spec.rb:

require 'spec_helper'

RSpec.describe Admin::BusinessesController, type: :controller do
  render_views

  before(:all) do
    @business = build_stubbed(:business)
  end

  describe '#update' do
    before(:each) do
      allow(Business).to receive(:find).and_return(@business)
      @new_name = Faker::Company.name
      @params = { id: @business.id, business: { name: @new_name } }
    end

    it 'updates business name' do
      expect(@business).to receive(:update_attributes).with(name: @new_name, confirm_flag: @business.confirm_flag, phone_primary: @business.phone_primary).and_return(true)
      xhr :patch, :update, id: @business.id, business: { name: @new_name }
      expect(@business.name).to eq(@new_name)
    end
  end
end

/app/controllers/admin/businesses_controller.rb

class Admin::BusinessesController < Admin::BaseAdminController

  def update
    @business.update_attributes(business_params)
  end

  def business_params
    params.require(:business).permit(:name, :active_flag).merge(confirm_flag: true, phone_primary: '')
  end

end

When I run this test I get this error:

Failures:

  1) Admin::BusinessesController#update updates business name
 Failure/Error: @business.update_attributes(business_params)

   #<Business:0x007fc43c4f5738> received :update_attributes with unexpected arguments
     expected: ({:name=>"Fadel, Larson and Hettinger", :confirm_flag=>true, :phone_primary=>"7832900175"})
          got: ({"name"=>"Fadel, Larson and Hettinger", "confirm_flag"=>true, "phone_primary"=>""})
   Diff:
   @@ -1,4 +1,4 @@
   -[{:name=>"Fadel, Larson and Hettinger",
   -  :confirm_flag=>true,
   -  :phone_primary=>"7832900175"}]
   +[{"name"=>"Fadel, Larson and Hettinger",
   +  "confirm_flag"=>true,
   +  "phone_primary"=>""}]

So it looks like the phone_primary is causing the problem and I change my test expectation line to be:

  describe '#update' do
    before(:each) do
      allow(Business).to receive(:find).and_return(@business)
      @new_name = Faker::Company.name
      @params = { id: @business.id, business: { name: @new_name } }
    end

    it 'updates business name' do
      expect(@business).to receive(:update_attributes).with(name: @new_name, confirm_flag: @business.confirm_flag, **phone_primary: ''**).and_return(true)
      xhr :patch, :update, id: @business.id, business: { name: @new_name }
      expect(@business.name).to eq(@new_name)
    end
  end

and i get the failure:

Failures:

  1) Admin::BusinessesController#update updates business name
 Failure/Error: expect(@business.name).to eq(@new_name)

   expected: "Flatley Inc"
        got: "Samara Kuhic"

And now the names aren't matching

2
Add .and_call_original to your stubbing of update_attributes. Right it doesn't do any updating and your later expectation fails, naturally.Sergio Tulentsev
im stubbing..not hitting the database on purposealisontague
You see, when you stub a method (on purpose!), you can't also expect it to do its job. Because you stubbed it. Having a cake and eating it too.Sergio Tulentsev
@SergioTulentsev so there's no way to stub an update method in RSpec?alisontague
There is a way, you're doing it. As I'm saying, you can't have a cake and eat it. You have to choose, which one you want.Sergio Tulentsev

2 Answers

2
votes

I ended up stubbing the reload by using assigns. My tests are now passing with this:

 describe '#update' do
   before(:each) do
     allow(Business).to receive(:find).and_return(@business)
     @new_name = Faker::Company.name
   end

   it 'updates business name' do
      expect(@business).to receive(:update_attributes).with(name: @new_name, confirm_flag: true, phone_primary: '').and_return(@business)
      xhr :patch, :update, id: @business.id, business: { name: @new_name }
      expect(assigns(:business)).to eq(@business)
      expect(response.status).to eq(200)
    end
  end

Not sure if this is the preferred way but it's passing for me. Looks like assigns will be deprecated in Rails 5

0
votes

If you check your original code, in your controller you have this:

def business_params
  params.require(:business).permit(:name, :active_flag).merge(confirm_flag: true, phone_primary: '')
end

you're always setting confirm_flag to true and phone_primary to ''(empty string). And in your test you have this:

expect(@business).to receive(:update_attributes).with(name: @new_name, confirm_flag: @business.confirm_flag, phone_primary: @business.phone_primary).and_return(true)

so, if you check confirm_flag you're setting it to @business.confirm_flag which in this case in fact is true and it's fine with what you're setting in the controller, but the problem comes with phone_primary which is being set to @business.phone_primary, and in this case @business.phone_primary is 7832900175, and that's why it is failing.

So, to fix this, you either have to change your test to:

expect(@business).to receive(:update_attributes).with(name: @new_name, confirm_flag: @business.confirm_flag, phone_primary: '').and_return(true)

or change your controller code to permit phone_primary from the params, which will also fix your error.