How to Test For (Activerecord) Object Equality

How to test for (ActiveRecord) object equality

Rails deliberately delegates equality checks to the identity column. If you want to know if two AR objects contain the same stuff, compare the result of calling #attributes on both.

Testing equality for ActiveRecord::Relation

It is worth noting that an ActiveRecord::Relation just defines a database query. It only holds information about where conditions, limit, joins etc. clauses. But that query is not performed until a result is actually requested.

Therefore it makes sense that two relations that would end up returning the same result under a certain condition are not treated as equal. Because running both queries under another condition might return different results: Maybe because they return timestamps or the return is randomly ordered.

The query is performed once a result is needed: When you call each or all for example. Or in your example: size or first. Once the query ran the actual result is returned (and not a Relation object anymore). Therefore all later comparisons return true.

ActiveRecord object equality

Answering my own question (which is irrelevant).

All the equality checks DO work as expected (and described in the docs).

I assume the reason it did not work for me is that I run autotest and something could be cached or some other mythical reason that I can't explain right now.

To summarise, all the following assertions are indeed passing:

a = Factory.create(:user)
b = User.find_by_email(a.email) # b is logically same as a

a.id.should == b.id
a.should == b
a.should eql b
User.find_by_email(a.email).should == User.find_by_email(a.email)
a.should == User.find_by_email(a.email)
b.should == User.find_by_email(a.email)

Equality test on three or more objects

[a,b,c,d].any?{|x| x != a}

or

array.any?{|x| x != array.first}

Alternatively, the #all? method may read more intuitively for some:

array.all? {|x| x == array.first }

Comparing ActiveRecord objects with Rspec

A shorter way to write the above would be actual.attributes.except(:id, :updated_at, :created_at).

If you are using this a lot, you can always define your own matcher like this:

RSpec::Matchers.define :have_same_attributes_as do |expected|
match do |actual|
ignored = [:id, :updated_at, :created_at]
actual.attributes.except(*ignored) == expected.attributes.except(*ignored)
end
end

Put this into your spec_helper.rb you can now say in any example:

User.first.should have_same_attributes_as(User.last)

Good luck.



Related Topics



Leave a reply



Submit