leemour
leemour

Reputation: 12073

RSpec doesn't compare objects properly

I have the following spec:

require 'spec_helper'

describe Page do
  it "has a valid factory" do
    create(:page).should be_valid
  end

  it "is invalid without a title" do
    build(:page, title: nil).should_not be_valid
  end

  it "finds record" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').should  == page
  end

  it "finds record with same attributes" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').first.attributes.each do |name, val|
      expect(page[name]).to eq(val)
    end
  end
end

I have the following factory:

model_statuses = ['published', 'draft']
FactoryGirl.define do
  factory :page do
    title         { Faker::Lorem.word + ' Page' }
    slug          { title ? title.parameterize : nil }
    intro         { Faker::Lorem.sentence 10 }
    content       { Faker::Lorem.sentences(5).join ' ' }
    status        { model_statuses.sample }
  end
end

Tests fail with:

Failures:

  1) Page finds record with same attributes
     Failure/Error: expect(page[name]).to eq(val)

       expected: Fri, 24 Jan 2014 23:33:47 MSK +04:00
            got: Fri, 24 Jan 2014 23:33:47 MSK +04:00

       (compared using ==)

       Diff:
     # ./spec/models/page_spec.rb:20:in `block (3 levels) in <top (required)>'
     # ./spec/models/page_spec.rb:19:in `each'
     # ./spec/models/page_spec.rb:19:in `block (2 levels) in <top (required)>'

  2) Page finds record
     Failure/Error: Page.unscoped.where(:title => 'heyo').should  == page
       expected: #<Page id: 1, slug: "heyo", title: "heyo", intro: "Sed sint et nesciunt earum libero eveniet est cupid...", content: "A sunt ab exercitationem quas ex incidunt numquam. ...", created_at: "2014-01-24 19:33:47", updated_at: "2014-01-24 19:33:47", status: "draft">
            got: [#<Page id: 1, slug: "heyo", title: "heyo", intro: "Sed sint et nesciunt earum libero eveniet est cupid...", content: "A sunt ab exercitationem quas ex incidunt numquam. ...", created_at: "2014-01-24 19:33:47", updated_at: "2014-01-24 19:33:47", status: "draft">] (using ==)

Why these objects and their attributes aren't the same and how do I properly check for object equality?

Upvotes: 0

Views: 1031

Answers (3)

vee
vee

Reputation: 38645

I see the problems to be in comparison of different objects in your tests.

The following test:

  it "finds record" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').should  == page
  end

When you get to this test you already have one page object from "has a valid factory" test. So after this test executes you have two page objects, first with title nil and second with title heyo. Now when you try to match with should in the next line your Page.unscoped.where(:title => 'heyo') does return expected result, i.e. an array of Page objects with title 'heyo'. You're getting an array and you're comparing the array with an Object, so that's going to fail.

Then the reason your second test "finds record with same attributes" is failing is because you are expecting the first Page object to be equal to last Page object, so this is also going to fail.

So the fixes would be on the following two tests:

  it "finds record" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').last.should  == page
  end

  it "finds record with same attributes" do
    page = create(:page, title: 'heyo')
    Page.unscoped.where(:title => 'heyo').last.attributes.each do |name, val|
      expect(page[name]).to eq(val)
    end
  end

Note the use of last in both tests.

The database_cleaner gem is something that you might be interested in. It allows you to clean the database before each tests. Recommend looking at it.

Upvotes: 2

TreyE
TreyE

Reputation: 2749

The source of both errors is the same.

You are comparing one of the dates, created_at, updated_at, and they don't seem to have the same value.

Notice that the dates only go to the second when printed as strings - they might differ at a lower level than that.

Either exclude the dates from the comparison, or do a raise/inspect in the code at the site of the failure to see if the fractional seconds are differing between the two items.

Another trick you can use is to stub out Time.now/Time.new within the scope of your spec, so that you will always get a consistent result.

Normally you will stub out/abuse Time.now in a before :each block inside the context of your spec. You want to make the scope as narrow as possible because overriding that method can have all sorts of weird side effects if you aren't careful where you do it.

Upvotes: 1

apneadiving
apneadiving

Reputation: 115541

You must freeze the time in your case, use timecop for instance

Upvotes: -1

Related Questions