2
votes

I'm trying to test the deletion of an association. The two models involved are User and Cancellation:

    class Cancellation < Active Record::Base
      belongs_to :taker, class_name: "User"
    end

    class User < ActiveRecord::Base
      has_many :taken_cancellations, class_name: "Cancellation", foreign_key: :taker_id
    end

In my test, I've got the following code:

describe "Admin Calendar" do
  subject { page } 

  let!(:user) { FactoryGirl.create(:user, name: "Taker User") }
  let(:cancellation) { FactoryGirl.create(:cancellation, start_at: 2.days.from_now, taker: user) }

  before do
    sign_in admin
    visit edit_admin_cancellation_path cancellation
  end

  #...

  describe "Edit Page" do
    it 'Making it available' do
      expect { click_link "Make it available" }.to change(cancellation.reload, :taker).from(user).to nil
    end
  end
end

click_link 'Make it available will trigger user.taken_cancellations.delete @cancellation in the controller.

The test fails with this error:

taker should have been changed to nil, but is now #<User id: 1, name: "Taker User"...

from the guides for the let method: https://relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let

The value will be cached across multiple calls in the same example but not across examples.

To me, it looks like that the change method is creating a new cancellation after the action instead of looking at the memoized one. I wasn't expecting this behavior.

The test passes when using a block, like so:

expect { click_link "Make it available" }.to change { cancellation.reload.taker }.from(user).to nil

Can anybody explain if I'm correct? Why these different behaviors?

1

1 Answers

3
votes

In the first form, cancellation.reload is evaluated once and the resulting object sent the message taker before and after the invocation of the click_link method. In other words, the cancellation object is not reloaded after click_link is called.

In the second form, cancellation.reload.taker is evaluated in its entirety before and after the call to click_link, so the reload has a chance to take effect after the database update has taken place.

Note, though, that depending on your configuration, the server that is processing the result of click_link may be executing in a separate thread, so in any event there is a race condition between the update of the database and the test assertion.