clem
clem

Reputation: 3534

RSpec + FactoryGirl should_receive failing

I can't figure out why this RSpec test fails. Any advice? I'm new-ish to FactoryGirl, RSpec, and TDD in general.

Controller:

def update
  @vendor = current_user.vendors.find(params[:id])

  if @vendor.update_attributes(params[:vendor])
    redirect_to vendor_path(@vendor)
  else
    render 'edit'
  end
end

Test:

require 'spec_helper'

describe VendorsController do
  login_user

  before :each do
    @user = subject.current_user
    @vendor = FactoryGirl.create(:vendor, :user => @user)
  end

  [...]

  describe 'POST update' do
    def do_update
      post :update, :id => @vendor.id, :vendor => FactoryGirl.attributes_for(:vendor)
    end

    [...]

    it 'should update a given vendor' do
      do_update
      @vendor.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor))
    end
  end
end

Factory:

FactoryGirl.define do
  factory :vendor do
    name 'Widget Vendor'
    user
  end
end

The Failure:

Failures:

  1) VendorsController POST update should update a given vendor
     Failure/Error: @vendor.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor))
       (#<Vendor:0x007faeb75e77d0>).update_attributes({:name=>"Widget Vendor"})
           expected: 1 time
           received: 0 times
     # ./spec/controllers/vendors_controller_spec.rb:108:in `block (3 levels) in <top (required)>'

Update:

I'm a little closer, now. I changed the test to the following:

it 'should update a given vendor' do
  Vendor.any_instance.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor))
  do_update
end

And the new error is:

Failures:

  1) VendorsController POST update should update a given vendor
     Failure/Error: post :update, :id => @vendor.id, :vendor => FactoryGirl.attributes_for(:vendor)
       #<Vendor:0x007ff30d765900> received :update_attributes with unexpected arguments
         expected: ({:name=>"Widget Vendor"})
              got: ({"name"=>"Widget Vendor"})
     # ./app/controllers/vendors_controller.rb:33:in `update'
     # ./spec/controllers/vendors_controller_spec.rb:98:in `do_update'
     # ./spec/controllers/vendors_controller_spec.rb:108:in `block (3 levels) in <top (required)>'

Answer...?

Well, this worked. There has to be a better way of doing this, though:

Vendor.any_instance.should_receive(:update_attributes).with(JSON.parse(FactoryGirl.attributes_for(:vendor).to_json)).and_return(true)

Upvotes: 0

Views: 2533

Answers (3)

Mark Cheverton
Mark Cheverton

Reputation: 1212

You can use the Hash stringify keys method in rails:

Vendor.any_instance.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor).stringify_keys)

Upvotes: 0

sparrovv
sparrovv

Reputation: 7824

I think you are doing it wrong.

The @vendor object in specs is another one that in your controller, so it doesn't receive "update_attributes" method.

You can try this (rspec 2.5+ probably):

Vendor.any_instance.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor))

Or you can check if object attributes has changed:

expect{
  do_update
}.to change(...)

Upvotes: 2

Dylan Markow
Dylan Markow

Reputation: 124469

I believe you need to set your expectations before posting the request; otherwise, by the time it hits your expectation the object has already been set. So move do_update after your should_receive line:

it 'should update a given vendor' do
  @vendor.should_receive(:update_attributes).with(FactoryGirl.attributes_for(:vendor))
  do_update
end

Upvotes: 1

Related Questions