如何在 RSpec 中比较两个哈希值?

12

我有两个哈希表 h1h2 ,我想在 RSpec 中进行比较。

我想要检查在某些转换(我们称之为 f)之后,h1 的元素是否与 h2 相同。也就是说,我想要验证对于 h1 中的每个键 kh1[k] == f(h2[k])

例如,如果 h2 中所有的值都是对应的 h1 值的两倍大小,那么我想要检查对于 h1 中的每个键 kh2[k] == h1[k] * 2

在 RSpec 中应该如何正确执行此操作?我目前的做法是:

h1 = ...
expect(
  h2.all? { |k,v|
    v == f(h1[k])
  }
).to be true

但那似乎很笨拙。


我会创建一个辅助方法 equal?(other) 来进行计算 -- 然后,expect(h2.equal?(h1)).to be true - onebree
2
你的问题非常误导人。在你的解释和代码中,你混淆了 h1h2 的角色。 - sawa
此外,您的描述(以及代码)表明匹配是单向的,即,如果解释文本中的h2具有与h1不同的键,则无关紧要。这是您的意图吗? - sawa
6个回答

7

听起来你正在测试转换。我建议编写类似以下的代码:

it "transforming something does something" do
  base_data = { k1: 1, k2: 2 }
  transformed_data = base_data.each_with_object({}) { |(k, v), t|
    t[k] = f(v)
  }

  expect(transformed_data).to eq(
    k1: 2,
    k2: 4,
  )
end

在我看来,这个描述清楚地说明了我们期望的内容。然后,我可以从测试中轻松地看到输入和期望的输出。此外,这利用了哈希匹配器,在失败时提供两个哈希之间的良好差异:

expected: {:k1=>2, :k2=>4}
     got: {:k1=>1, :k2=>4}

(compared using ==)

Diff:
@@ -1,3 +1,3 @@
-:k1 => 2,
+:k1 => 1,
 :k2 => 4,

虽然我会质疑键值关系的含义。这些只是您要运行的测试用例吗?如果是这样,我会为每个测试用例创建一个唯一的测试。如果有更多的东西,那么我可能会质疑为什么不提供转换方法以开始哈希。


4

如果有人正在寻找更好的大型嵌套哈希差异比较工具,我已经想到了以下解决方案:

RSpec::Matchers.define :be_a_hash_like do |expected_hash|
  match do |actual_hash|
    matching_results = actual_hash == expected_hash
    unless matching_results
      system(
        "git diff $(echo '#{JSON.pretty_generate(expected_hash)}' | git hash-object -w --stdin) " +
                 "$(echo '#{JSON.pretty_generate(actual_hash)}' | git hash-object -w --stdin) --word-diff",
        out: $stdout,
        err: :out
      )
    end
    matching_results
  end

  failure_message { 'Look at the Diff above! ^^^' }
end

# then use:
expect(actual).to be_a_hash_like(expected)

原始代码片段:https://gist.github.com/fabriciofreitag/b7458725ffef08eaf5ea541c95385a92

输出示例:在这里输入图片描述


2
我们正在使用hashdiff来解决RSpec中的这个问题。
我们开发了一个自定义匹配器:
RSpec::Matchers.define :match_hash do |expected|
  match do |actual|
    Hashdiff.best_diff(expected, actual).blank?
  end
  failure_message do |actual|
    "expected that #{actual} would match #{expected}, but it has these differences: #{Hashdiff.best_diff(expected, actual)}"
  end
end

我们可以这样使用它:expect(actual_hash).to match_hash(expected_hash)

1
h1.each do |k, v|
  expect(v).to eq(f(h2[k]))
end

关于我来说,这似乎更易读。

1
怎么样:
h2 = f(h1)

expect(h2.keys).to eq(h1.keys)  # I assume you want this, too

h1.keys.each do |k|
  expect(h2[k]).to eq(h1[k] * 2) 
end

有点啰嗦,但可能更易读?

0

对于精确相等:

expect(h1).to eq h2.map { |k, v| [k, f(v)] }.to_h

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接