symbole non désiré à la conversion de chaîne de clé de hachage
-
30-09-2019 - |
Question
Quand j'ASSIGN mon contrôleur
@my_hash = { :my_key => :my_value }
et vérifiez que le contrôleur en faisant
get 'index'
assigns(:my_hash).should == { :my_key => :my_value }
alors je reçois le message d'erreur suivant:
expected: {:my_key=>:my_value},
got: {"my_key"=>:my_value} (using ==)
Pourquoi ce symbole automatique à la conversion de chaîne se produire? Pourquoi affecte-t-elle la clé du hachage?
La solution
Il peut finir comme HashWithIndifferentAccess
si Rails obtient en quelque sorte Ahold, et que les utilisations chaîne interne clés. Vous pouvez vérifier la classe est le même:
assert_equal Hash, assigns(:my_hash).class
Les paramètres sont toujours traités comme le type d'accès indifférent de hachage de sorte que vous pouvez récupérer en utilisant soit la chaîne ou le symbole. Si vous affectez ceci à votre params hachage sur le get
ou composez le post
, ou vous pourriez se convertir.
Une autre chose que vous pouvez faire est de geler et de voir si les tentatives de quiconque de le modifier parce que devrait lancer une exception:
@my_hash = { :my_key => :my_value }.freeze
Autres conseils
Vous pouvez essayer d'appeler "stringify_keys":
assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys
AHA! Cela se passe pas à cause de rails, en soi, mais à cause de Rspec.
J'ai eu le même problème de tester la valeur d'un Hashie::Mash
dans une spécification du contrôleur (mais il applique à tout ce qui caquette comme un Hash
)
Plus précisément, dans une spécification du contrôleur, lorsque vous appelez assigns
pour accéder aux variables d'instance définies dans l'action du contrôleur, il ne revient pas exactement la variable d'instance définie, mais plutôt, une copie de la variable qui stocke Rspec en tant que membre un HashWithIndifferentAccess
(contenant toutes les variables d'instance attribuées). Malheureusement, lorsque vous tenez un Hash
(ou quoi que ce soit qui hérite de Hash
) dans un HashWithIndifferentAccess
, il est automatiquement converti en une instance de même, oh-so-pratique, mais pas tout à fait exact classe:)
Le plus de travail autour est d'éviter la conversion en accédant directement à la variable, avant qu'il ne soit converti « pour votre commodité », en utilisant: controller.view_assigns['variable_name']
(note: la clé ici doit être une chaîne, pas un symbole)
le test dans le message original doit passer si elle a été changée en:
get 'index'
controller.view_assigns['my_hash'].should == { :my_key => :my_value }
(bien sûr, .should
ne prend plus en charge les nouvelles versions de RSpec, mais juste pour la comparaison je l'ai gardé le même)
Voir cet article pour plus d'explications: http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html
Je sais que cela est vieux, mais si vous mettez à niveau de Rails 3 à 4, vos tests de contrôleur peut encore avoir des endroits où Hash
avec les touches de symbole a été utilisé, mais par rapport à la version de chaîne de caractères, juste pour éviter l'attente erronée.
Rails-4 a résolu ce problème: https://github.com/rails/rails / pull / 5082 . Je suggère de mettre à jour vos tests pour avoir des attentes contre les clés réelles.
Dans Rails-3 la méthode assigns
convertit votre @my_hash
à HashWithIndifferentAccess
que stringifies toutes les clés -
def assigns(key = nil)
assigns = @controller.view_assigns.with_indifferent_access
key.nil? ? assigns : assigns[key]
end
Rails-4 mis à jour pour retourner les clés d'origine -
def assigns(key = nil)
assigns = {}.with_indifferent_access
@controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
key.nil? ? assigns : assigns[key]
end
Vous pouvez également passer votre objet Hash
à l'initialiseur de HashWithIndifferentAccess
.
Vous pouvez utiliser HashWithIndifferentAccess.new
comme init Hash:
Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail@somehost.com', from: 'from@host.com')