在测试中访问哈希值

Accessing Hash Value in Test

我正在学习 Hanami 教程,但我无法弄清楚此测试中出了什么问题:

describe Web::Controllers::Books::Create do
  let(:action) { Web::Controllers::Books::Create.new }
  let(:params) { Hash[book: { title: 'Confident Ruby', author: 'Avdi Grimm'  }] }

  it 'creates a new book' do 
    action.call(params)

    action.book.id.wont_be_nil
    action.book.title.must_equal params[:book][:title]
  end
end

rake test 导致失败,因为 "Confident Ruby" 不等于 nil。

我可以在action.call之后puts params[:book],但是params[:book][:title]果然是nil。我尝试通过其他方式访问 title 但似乎无法管理它。看来 params[:book][:title] 应该是正确的。

但是,当我使用 params[:book]['title'] 时,它似乎有效。如果我尝试在 IRB 中创建一个参数散列,params[:book][:title] 有效,而 params[:book]['title'] 无效,所以我仍然感到困惑。

我已经升级到 Ruby 2.3.0,但仍然遇到同样的问题。

params[:book].has_key?(:title)falseparams[:book].has_key?('title')true。交叉引用有关如何访问哈希元素的文档,我只是不明白。

这里发生了什么?

这绝不是一个 新手 问题,因为许多有经验的 Ruby 爱好者以各种方式与这个话题作斗争。简短的回答是,这不是 正常的 哈希行为,而是 Rails 哈希操作的一个已知问题。您对 Hash 的 irb 体验是真实的 Ruby 行为,并且符合您的期望。

所以,真正的问题是,"why does Rails muck with hashes to make them misbehave?"这是为支持相当多的用例而做出的一系列很长很长的决定,包括对参数哈希、会话哈希和 ActiveRecord 字段名称的支持(例如批量分配)。此功能的最终根源是 Rails class HashWithIndifferentAccess.

HashWithIndifferentAccess 所做的是为符号(例如 :book:title)和字符串(例如 'book'、'title')提供散列键的等效性,这样您就不需要知道需要哪种类型的密钥。因此,indifferent access。实际上,符号键在访问点内部转换为字符串。它确实解决了一个实际问题,特别是对于 Ruby 和 Rails 的新程序员。然而,在一些最奇怪的地方出现了副作用。 RSpec 恰好是那些奇怪的地方之一,并且有各种这些副作用的记录示例。 似乎正在发生的事情是原始哈希被转换为 HashWithIndifferentAccess,它本身是从 Hash 派生的。一旦你有了 HashWithIndifferentAccess 的实例,你通常可以像 Hash 一样对待它,但你必须小心不要将它转换或合并到另一个 Hash(或任何其他派生的Hash)。问题在于无关紧要的键,不明确支持 HashWithIndifferentAccess 的代码会顺便复制、克隆或合并键的字符串形式,从而在过程中丢失符号键的概念。

HashWithIndifferentAccess 的问题在嵌套哈希中最常遇到,展示了您遇到的确切行为。简而言之,顶级哈希键仍然可以使用符号访问,而嵌套哈希只能使用字符串键访问。

您已经确定了问题的解决方案,那就是使用字符串键从 RSpec 中访问嵌套哈希;这个答案只是为了将原因联系在一起。将 RSpec 散列键定义为字符串而不是符号可能是个好主意,而且启动起来更容易混淆,这样您 就知道 字符串键是正确的要使用的键。

如需了解更多信息,您可能有兴趣阅读其中的一些文章:

抱歉,这是一个错误,现在已修复。所有参数都可以像这样访问:

params[:book][:title]