thedanotto
thedanotto

Reputation: 7307

rails rspec testing updating attribute on model

I have a very simple model called Reminder with a boolean attribute of verified and I want to test my method update_verified which simply sets the attribute verified to true. I'm using rspec and factory girl.

# reminder.rb
  def update_verified
    self.update(verified: true)
  end 

# reminder_spec.rb
  describe "#update_verified" do
    it "should mark the reminder.verified to true" do
      reminder = build(:reminder, verified: false)
      reminder.update_verified

      expect(reminder.verified).to eq(true)
    end
  end

For some reason, when I run the test, the new value of true for the attribute verified is not being persisted. However, when I run the method in console, it works fine. Thoughts?

  1) Reminder#update_verified should mark the reminder.verified to true
     Failure/Error: expect(reminder.verified).to eq(true)

       expected: true
            got: false

       (compared using ==)
     # ./spec/models/reminder_spec.rb:46:in `block (3 levels) in <top (required)>'

Finished in 0.19193 seconds (files took 7.07 seconds to load)

Upvotes: 12

Views: 29435

Answers (7)

Frank Fang
Frank Fang

Reputation: 2162

This works for me

expect do
  reminder.update_verified
  reminder.reload
end.to change(reminder, :verified).from(false).to(true)

Upvotes: 10

Nickolay Efimov
Nickolay Efimov

Reputation: 39

Your object is not persisted yet, when you first time call reminder.update_verified. Instead of update, it will just create an object with assigned attributes. When you call it for a second time, it will work properly, because object is persisted. You need to change build to create and it's gonna work.

Upvotes: 1

Andreas
Andreas

Reputation: 998

I found this helpful to check if the method saves changes to an object in the database:

expect { reminder.update_verified }.to change(reminder, :updated_at)

Explanation

Calling self.update(verified: true) does not only update the verified-column, but also the updated_at-column. We can then check for changes in the updated_at-column to verify that changes to the object has been saved.

Upvotes: 7

twindai
twindai

Reputation: 196

update_column can always work no matter you use let or build

Upvotes: 2

Aleksey
Aleksey

Reputation: 2309

You need to call reload on object.
And please follow rspec best practices: use let and subject.

describe "#update_verified" do
  let(:reminder) { build(:reminder, verified: false) }
  subject { reminder.update_verified }
  it "should mark the reminder.verified to true" do
    subject
    expect(reminder.reload.verified).to eq(true)
  end
end

UPDATE
The problem is in build method. It creates new object and does not save object to DB.
Replace build with create method.

Upvotes: 2

oreoluwa
oreoluwa

Reputation: 5623

That's quite strange, but I think you should change from the build(:reminder, verified: false) to create(:reminder, verified: false) then use the reload method as others have specified above.

Upvotes: 2

Anthony
Anthony

Reputation: 15957

You just need to reload your object:

expect(reminder.reload.verified).to eq(true)

Upvotes: 23

Related Questions