如何测试 (ActiveRecord) 对象相等性
在 Rails 3.0.3
上的 Ruby 1.9.2
中,我尝试测试两个 Friend
之间的对象相等性(类继承自 ActiveRecord::Base
) 对象。
对象是相等的,但测试失败:
Failure/Error: Friend.new(name: 'Bob').should eql(Friend.new(name: 'Bob'))
expected #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
(compared using eql?)
只是为了笑,我还测试了对象身份,这正如我所期望的那样失败了:
Failure/Error: Friend.new(name: 'Bob').should equal(Friend.new(name: 'Bob'))
expected #<Friend:2190028040> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend:2190195380> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
'actual.should == expected' if you don't care about
object identity in this example.
有人可以向我解释为什么对象相等的第一个测试失败,以及我如何成功断言这些对象两个物体相等吗?
In Ruby 1.9.2
on Rails 3.0.3
, I'm attempting to test for object equality between two Friend
(class inherits from ActiveRecord::Base
) objects.
The objects are equal, but the test fails:
Failure/Error: Friend.new(name: 'Bob').should eql(Friend.new(name: 'Bob'))
expected #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
(compared using eql?)
Just for grins, I also test for object identity, which fails as I'd expect:
Failure/Error: Friend.new(name: 'Bob').should equal(Friend.new(name: 'Bob'))
expected #<Friend:2190028040> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend:2190195380> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
'actual.should == expected' if you don't care about
object identity in this example.
Can someone explain to me why the first test for object equality fails, and how I can successfully assert those two objects are equal?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
Rails 特意将相等性检查委托给标识列。如果您想知道两个 AR 对象是否包含相同的内容,请比较对两者调用 #attributes 的结果。
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.
查看
上的 API 文档 ==
(别名eql?
)用于ActiveRecord::Base
的操作Take a look at the API docs on the
==
(aliaseql?
) operation forActiveRecord::Base
如果您想根据属性比较两个模型实例,您可能需要从比较中排除某些不相关的属性,例如:
id
、created_at< /code> 和
updated_at
。 (我认为这些更多的是关于记录的元数据,而不是记录数据本身的一部分。)当您比较两个新的(未保存的)记录时,这可能并不重要(因为
id、
created_at
和updated_at
在保存之前都将是nil
),但有时我发现有必要比较已保存的< /em> 对象有一个未保存(在这种情况下 == 会给你 false,因为 nil != 5)。或者我想比较两个保存的对象以查明它们是否包含相同的数据(因此 ActiveRecord==
运算符不起作用,因为如果它们具有不同的id
,即使它们在其他方面相同,它也会返回 false)。我对这个问题的解决方案是在您想要使用属性进行比较的模型中添加类似的内容:
然后在我的规范中我可以编写这样可读且简洁的内容:
我计划分叉
active_record_attributes_equality< /code> gem 并将其更改为使用此行为,以便可以更轻松地重用它。
不过,我的一些问题包括:
==
运算符不是一个好主意,所以现在我将其称为identical?
。但也许类似practically_identical?
或attributes_eql?
会更准确,因为它不会检查它们是否严格相同(某些 的属性允许不同。)...attributes_to_ignore_when_comparing
太冗长了。如果他们想要使用 gem 的默认值,则不需要将其显式添加到每个模型中。也许允许使用像ignore_for_attributes_eql :last_signed_in_at, :updated_at
这样的类宏覆盖默认值,欢迎发表评论...
更新:而不是分叉
active_record_attributes_equality,我编写了一个全新的 gem,active_record_ignored_attributes,可在 http://github .com/TylerRick/active_record_ignored_attributes 和 http://rubygems.org/gems/active_record_ignored_attributes
If you want to compare two model instances based on their attributes, you will probably want to exclude certain irrelevant attributes from your comparison, such as:
id
,created_at
, andupdated_at
. (I would consider those to be more metadata about the record than part of the record's data itself.)This might not matter when you are comparing two new (unsaved) records (since
id
,created_at
, andupdated_at
will all benil
until saved), but I sometimes find it necessary to compare a saved object with an unsaved one (in which case == would give you false since nil != 5). Or I want to compare two saved objects to find out if they contain the same data (so the ActiveRecord==
operator doesn't work, because it returns false if they have differentid
's, even if they are otherwise identical).My solution to this problem is to add something like this in the models that you want to be comparable using attributes:
Then in my specs I can write such readable and succinct things as this:
I'm planning on forking the
active_record_attributes_equality
gem and changing it to use this behavior so that this can be more easily reused.Some questions I have, though, include:
==
operator is a good idea, so for now I'm calling itidentical?
. But maybe something likepractically_identical?
orattributes_eql?
would be more accurate, since it's not checking if they're strictly identical (some of the attributes are allowed to be different.)...attributes_to_ignore_when_comparing
is too verbose. Not that this will need to be explicitly added to each model if they want to use the gem's defaults. Maybe allow the default to be overridden with a class macro likeignore_for_attributes_eql :last_signed_in_at, :updated_at
Comments are welcome...
Update: Instead of forking the
active_record_attributes_equality
, I wrote a brand-new gem, active_record_ignored_attributes, available at http://github.com/TylerRick/active_record_ignored_attributes and http://rubygems.org/gems/active_record_ignored_attributes我在 RSpec 上创建了一个匹配器专门用于这种类型的比较,非常简单,但有效。
在这个文件里面:
spec/support/matchers.rb
您可以实现此匹配器...
之后,您可以通过以下方式在编写规范时使用它...
有用的链接:
https://relishapp.com/rspec/rspec-expectations/ v/2-4/docs/custom-matchers/define-matcher
https://github.com/thoughtbot/factory_bot
I created a matcher on RSpec just for this type of comparison, very simple, but effective.
Inside this file:
spec/support/matchers.rb
You can implement this matcher...
After that, you can use it when writing a spec, by the following way...
Useful links:
https://relishapp.com/rspec/rspec-expectations/v/2-4/docs/custom-matchers/define-matcher
https://github.com/thoughtbot/factory_bot
如果像我一样,您正在寻找这个问题的Minitest答案,那么这里有一个自定义方法,它断言两个对象的属性是相等的。
它假定您始终希望排除
id
、created_at
和updated_at
属性,但如果您愿意,可以覆盖该行为。我喜欢保持
test_helper.rb
干净,因此创建了一个包含以下内容的test/shared/custom_assertions.rb
文件。然后更改您的
test_helper.rb
以包含它,以便您可以在测试中访问它。基本用法:
如果要覆盖它忽略的属性,请使用
except
参数传递字符串或符号数组:If like me you're looking for a Minitest answer to this question then here's a custom method that asserts that the attributes of two objects are equal.
It assumes that you always want to exclude the
id
,created_at
, andupdated_at
attributes, but you can override that behaviour if you wish.I like to keep my
test_helper.rb
clean so created atest/shared/custom_assertions.rb
file with the following content.Then alter your
test_helper.rb
to include it so you can access it within your tests.Basic usage:
If you want to override the attributes it ignores then pass an array of strings or symbols with the
except
arg: