evfwcqcg
evfwcqcg

Reputation: 16335

How to get rid of necessarily reloading in RSpec?

I encountered a strange behavior when I run specs.

This code doesn't work (baz.viewed is not updated) unless I uncomment line baz.reload.

describe "#..." do
  it "..." do
    baz = user.notifications.create!(title: "baz")
    baz.update_attribute(:created_at, Time.now + 3.day)

    # it sets `viewed` to `true` in the model to which `baz` is referred.
    user.dismiss_latest_notification!

    # baz.reload
    baz.viewed.should == true
  end
end

I don't run specs using Spork or Guard, but this model isn't reloaded anyway.

Why it might happening? Or, is it normal practice to invoke .reload methods in specs?

Upvotes: 0

Views: 262

Answers (1)

Erez Rabih
Erez Rabih

Reputation: 15788

Let me make this case a bit clearer for you: When the line baz = user.notifications.create!(title: "baz") gets executed, two things happen:

1- A new notification row is added to the DB.

2- An object is created in memory, representing this row, and may be referenced with the variable baz. Note that baz has a viewed value of false (and so does the row in the meantime).

Now I didn't see the actual implementation of the method

user.dismiss_latest_notification!

But since you are not passing any variable to it I surely know you have a code in the spirit of:

def dismiss_latest_notification!
  latest_notification = self.notifications.last
  latest_notification.viewed = true
  latest_notification.save!
end

The important line here is

   latest_notification = self.notifications.last

An object is created in memory, representing the same row baz does, but stored in another variable - latest_notification.

Now you have two variables representing the same row in DB. When you perform the save on latest_notification the DB is updated with the right viewed value but the variable baz is not updated in any manner to reflect this change. You have no choice but to force the update from the DB with the latest values by executing reload on it.

I think the right way to get rid of the reloading is to change the test a little bit:

Instead of

baz.viewed.should == true

Use:

user.notifications.last.viewed.should be_true

In my opinion, it better suits the purpose of this specific test.

Upvotes: 4

Related Questions