Comment tester quelle validation a échoué dans ActiveRecord?
J'ai un modèle comme ceci:
class User < ActiveRecord::Base
validates_length_of :name, :in => (2..5)
end
Je veux tester cette validation:
it "should not allow too short name" do
u = User.new(:name => "a")
u.valid?
u.should have(1).error_on(:name)
end
Mais alors il ne teste pas quel type d'erreur a été défini sur name
. Je veux savoir si c'était le cas too_short
, too_long
, ou peut-être qu'une autre validation a échoué.
Je peux rechercher le texte du message dans le tableau errors, comme ceci:
u.errors[:name].should include(I18n.t("activerecord.errors.models.user.attributes.name.too_short"))
Mais cela échouera lorsque je définirai activerecord.errors.messages.too_short
dans un fichier de paramètres régionaux au lieu d'un message spécifique au modèle.
Alors, est-il possible de vérifier quel type d'erreur s'est produite?
4 réponses
Rails a ajouté une méthode pour rechercher des erreurs dans ActiveModel à la fin de 2011 et Rails v3. 2. Il suffit de vérifier pour voir si l'erreur a été #added?
:
# An error added manually
record.errors.add :name, :blank
record.errors.added? :name, :blank # => true
# An error added after validation
record.email = 'taken@email.com'
record.valid? # => false
record.errors.added? :email, :taken # => true
Notez que pour les validations paramétrées (par exemple :greater_than_or_equal_to
), vous devrez également passer la valeur du paramètre.
record.errors.add(:age, :greater_than_or_equal_to, count: 1)
record.errors.added?(:age, :greater_than_or_equal_to, count: 1)
Les erreurs Sont identifiées par leur clé i18n. Vous pouvez trouver les clés appropriées pour vérifier dans le fichier Rails i18n approprié pour n'importe quelle langue sous l'erreur section .
Certains autres astucieux questions que vous pouvez poser ActiveModel#Error
sont #empty?
et
#include?(attr)
, ainsi que tout ce que vous pouvez demander à un Enumerable
.
Je n'aime vraiment pas l'idée de chercher des messages d'erreur traduits dans le hachage des erreurs. Après une conversation avec un autre Rubyists, j'ai terminé le hachage des erreurs de correction de singe, de sorte qu'il enregistre le message non traduit en premier.
module ActiveModel
class Errors
def error_names
@_error_names ||= { }
end
def add_with_save_names(attribute, message = nil, options = {})
message ||= :invalid
if message.is_a?(Proc)
message = message.call
end
error_names[attribute] ||= []
error_names[attribute] << message
add_without_save_names(attribute, message, options)
end
alias_method_chain :add, :save_names
end
end
Ensuite, vous pouvez tester comme ceci:
u = User.new(:name => "a")
u.valid?
u.errors.error_names[:name].should include(:too_short)
Je recommande de vérifier la gem shoulda pour gérer ces types de tests de validation répétitifs. Il complète RSpec ou Test:: Unit afin que vous puissiez écrire des spécifications concises telles que:
describe User do
it { should ensure_length_of(:name).is_at_least(2).is_at_most(5) }
end
L'approche que j'utilise:
it "should not allow too short name" do
u = User.new(:name => "a")
expect{u.save!}.to raise_exception(/Name is too short/)
end
J'utilise une expression rationnelle pour correspondre au message d'exception car il peut y avoir beaucoup de messages de validation dans le message d'exception, mais nous voulons nous assurer qu'il contient un extrait spécifique relatif au nom étant trop court.
Cette approche couple vos assertions à vos messages de validation, donc si vous modifiez chaque message de validation, vous devrez probablement modifier vos spécifications. Dans l'ensemble cependant, c'est un moyen simple d'affirmer des validations font leur travail.