Comment faire pour tester (ActiveRecord) objet de l'égalité
Dans Ruby 1.9.2
sur Rails 3.0.3
, je suis tenté de tester pour objet l'égalité entre les deux Friend
(classe hérite de ActiveRecord::Base
) des objets.
Les objets sont égaux, mais le test échoue:
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?)
Juste pour sourire, j'ai aussi tester pour l'identité de l'objet, qui ne parvient pas comme je l'avais prévu:
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.
Quelqu'un peut m'expliquer pourquoi le premier test pour objet l'égalité échoue, et comment je peux réussir à faire valoir ces deux objets sont égaux?
Vous devez vous connecter pour publier un commentaire.
Rails délibérément délégués à l'égalité des contrôles de la colonne d'identité. Si vous voulez savoir si deux AR objets contiennent la même substance, de comparer le résultat de l'appel #attributs sur les deux.
nil
pour les deux instances, car aucun des deux n'a été enregistré.eql?()
vérifie à la fois la valeur et le type de l'attribut.nil.class == nil.class
esttrue
etnil == nil
esttrue
, donc, OP premier exemple doit toujours avoir réussi vrai. Votre réponse n'explique pas pourquoi il est de retour faux.Prendre un coup d'oeil à la Les docs de l'API sur le
==
(aliaseql?
) opération pourActiveRecord::Base
eql?
est remplacé, mais pas l'aliasequal?
qui se compare toujoursobject_id
==
explique l'essentiel de tout ce que j'ai besoin de savoir, de comprendre comment les rails a été objet de test de l'égalité.Si vous voulez comparer deux instances de modèle en fonction de ces attributs, vous aurez probablement envie de exclure certains attributs non pertinents à partir de votre titre de comparaison, tels que:
id
,created_at
, etupdated_at
. (Je considère ceux à être plus métadonnées au sujet de l'enregistrement de la partie de l'enregistrement de données elle-même.)Ce n'est pas pour déplaire lors de la comparaison de deux nouvelles (non enregistré) dossiers (depuis
id
,created_at
, etupdated_at
seront tousnil
jusqu'enregistré), mais je trouve parfois qu'il faut comparer un enregistré objet avec une non enregistrées un (dans ce cas == souhaitez-vous donner des faux, puisque nul != 5). Ou je veux comparer deux enregistré objets pour savoir si elles contiennent le même données (donc le ActiveRecord==
opérateur ne fonctionne pas, parce qu'elle renvoie false si elles ont desid
's, même si elles ne sont pas identiques).Ma solution à ce problème est d'ajouter quelque chose comme cela dans les modèles que vous souhaitez être comparable à l'aide d'attributs:
Puis dans mes specs je peux écrire de telles lisible et succincte les choses comme ceci:
J'ai le projet de faire un fork de la
active_record_attributes_equality
gem et de la modifier à utiliser ce comportement pour que cela puisse être plus facilement réutilisable.Quelques questions que j'ai, cependant, comprennent:
==
opérateur est une bonne idée, donc pour l'instant je suis en l'appelantidentical?
. Mais peut-être quelque chose commepractically_identical?
ouattributes_eql?
serait plus exact, car il n'est pas de vérifier si ils sont strictement identiques (certains les attributs ne sont pas autorisés à être différent.)...attributes_to_ignore_when_comparing
est trop verbeux. Non pas que cela doit être explicitement ajoutés à chaque modèle si ils veulent utiliser la gem, les valeurs par défaut. Peut-être permettre la valeur par défaut pour être remplacé par une classe de macro commeignore_for_attributes_eql :last_signed_in_at, :updated_at
Les commentaires sont les bienvenus...
Mise à jour: au Lieu de bifurquer le
active_record_attributes_equality
, j'ai écrit un tout nouveau joyau, active_record_ignored_attributes, disponible à l' http://github.com/TylerRick/active_record_ignored_attributes et http://rubygems.org/gems/active_record_ignored_attributesJ'ai créé un matcher sur RSpec juste pour ce type de comparaison, très simple, mais efficace.
À l'intérieur de ce fichier:
spec/support/matchers.rb
Vous pouvez mettre en œuvre cette matcher...
Après que, vous pouvez l'utiliser lors de la rédaction d'une spécification, de la façon suivante...
Liens utiles:
https://relishapp.com/rspec/rspec-expectations/v/2-4/docs/custom-matchers/define-matcher
https://github.com/thoughtbot/factory_bot