不需要的符号来转换哈希键的字符串

当我在我的控制器中分配时

@my_hash = { :my_key => :my_value } 

并通过执行测试该控制器

 get 'index' assigns(:my_hash).should == { :my_key => :my_value } 

然后我收到以下错误消息:

 expected: {:my_key=>:my_value}, got: {"my_key"=>:my_value} (using ==) 

为什么这种自动符号转换为字符串? 为什么它会影响哈希的键?

如果Rails以某种方式得到它并且在内部使用字符串键,它可能最终成为HashWithIndifferentAccess 。 您可能想要validation该类是否相同:

 assert_equal Hash, assigns(:my_hash).class 

参数始终作为无差别访问类型的哈希处理,因此您可以使用字符串或符号进行检索。 如果您在getpost调用中将此值分配给params哈希,或者您可能正在转换。

你可以做的另一件事是冻结它,看看是否有人试图修改它,因为这应该抛出exception:

 @my_hash = { :my_key => :my_value }.freeze 

您可以尝试调用“stringify_keys”:

 assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys 

AHA! 发生这种情况不是因为Rails本身,而是因为Rspec。

我在控制器规范中测试Hashie::Mash的值时遇到了同样的问题(但是它适用于任何像Hash一样Hashie::Mash东西)

具体来说,在控制器规范中,当您调用assigns来访问控制器操作中设置的实例变量时,它不会完全返回您设置的实例变量,而是返回HashWithIndifferentAccess作为HashWithIndifferentAccess成员存储的变量的副本(包含所有已分配的实例变量)。 不幸的是,当你将Hash (或任何inheritance自Hash东西) HashWithIndifferentAccessHashWithIndifferentAccess ,它会自动转换为相同的实例,非常方便但不太准确的类:)

最简单的解决方法是通过直接访问变量来避免转换,在“为了您的方便”转换之前,使用: controller.view_assigns['variable_name'] (注意:此处的键必须是字符串,而不是符号)

因此,如果将原始post中的测试更改为:

 get 'index' controller.view_assigns['my_hash'].should == { :my_key => :my_value } 

(当然,RSpec的新版本不再支持.should ,但仅仅为了比较我保持不变)

有关详细说明,请参阅此文章: http : //ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html

您还可以将Hash对象传递给HashWithIndifferentAccess的初始化HashWithIndifferentAccess

您可以使用HashWithIndifferentAccess.new作为Hash init:

 Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail@somehost.com', from: 'from@host.com')