使用 RSpec 测试嵌套哈希时随机排序数组的匹配

Matching of randomly ordered array when test nested hash with RSpec

提问人:23tux 提问时间:9/1/2023 最后编辑:mechnicov23tux 更新时间:9/1/2023 访问量:78

问:

在我的 RSpec 测试中,我经常遇到像这样比较深度嵌套哈希的挑战

{ foo: ["test", { bar: [1,2,3] }] }

这些值是从无法保证顺序的数据库中读取的,我也不关心顺序。因此,当我在测试中比较两个哈希值时,我总是必须确保双方都强制执行顺序:[1,2,3]

# my_class.rb
class MyClass
  def self.data
    read_db.sort
  end
end

expected_data = { foo: ["test", { bar: [1,2,3].sort }] }
expect(MyClass.data).to eq(expected_data)

我真的很不喜欢这样一个事实,即我必须更改我的生产代码只是因为我的测试环境。

当然,我可以停止比较整个哈希值并专注于单个键,因此可以删除生产代码中的自定义排序:

actual_data = MyClass.data
expect(actual_data.fetch(:foo)[0]).to eq("test")
expect(actual_data.fetch(:foo)[1].fetch(:bar)).to match_array([1,2,3])

但这使得整个规范非常复杂且难以阅读。

因此,我考虑创建一个自定义的“无序数组”类 Bag,当它进行比较时,它会忽略顺序:

class Bag < Array
  def eql?(other)
    sort_by(&:hash) == other.sort_by(&:hash)
  end
  alias == eql?
end

但这只有在类位于比较的左侧时才有效:Bag

expect(Bag.new([1, "2"])).to eq(["2", 1])

 1 example, 0 failures

但通常情况并非如此,因为测试中的预期值应该在 里面,它表示 DB 中的值:expect(...)

expect(["2", 1]).to eq(Bag.new([1, "2"]))

 Failure/Error: expect(["2", 1]).to eq(Bag.new([1, "2"]))
   expected: [1, "2"]
        got: ["2", 1]
   (compared using ==)

 1 example, 1 failure

这背后的原因是,这是调用的,而不是我的自定义方法。Array#==Bag#==

我查看了它所说的文档(https://devdocs.io/ruby~3.2/array#method-i-3D-3D

如果 array.size == other_array.size 和 array 中的每个索引 i array[i] == other_array[i],则返回 true:

但在这里我停了下来,因为我无法弄清楚如何实现特定索引的值的获取。我尝试实现 Bar#[]Bar#fetch,但在比较对象时没有调用它们。

也许这根本不可能,因为数组调用了一些无法覆盖的低级 C 函数。但也许有人知道解决方案。

数组 ruby rspec ruby-hash

评论


答:

1赞 mechnicov 9/1/2023 #1

我当然可以停止比较整个哈希值

您可以继续比较整个哈希值。RSpec 是非常强大的工具

RSpec 允许您直接在预期代码中使用其匹配器 DSL

因此,为什么不使用此功能呢?

此外,还有一个功能,我没有找到详细的文档:

这是对哈希使用匹配

RSpec.describe do
  let(:hsh) { { foo: ["test", { bar: [1,2,3] }] } }

  it 'check nested hashes well' do
    expect(hsh).to match(foo: contain_exactly("test", match(bar: contain_exactly(1, 2, 3))))
  end
end

此处,并在预期输出中使用。当然,您可以使用 like 或任何其他匹配器matchcontain_exactlymatch_arraymatch_array([1, 2, 3])

3赞 spickermann 9/1/2023 #2

我会像这样使用嵌套:matchmatch_array

it 'matches when nested arrays having different sorting' do
  data = { foo: ['test', { bar: [3, 2, 1] }] }

  expect(data).to match(
    { foo: ['test', bar: match_array([1, 2, 3])] }
  )
end