【问题标题】:Match multiple yields in any order以任意顺序匹配多个收益
【发布时间】:2014-08-20 23:10:19
【问题描述】:

我想使用 rspec 测试一个迭代器。在我看来,唯一可能的产量匹配器是yield_successive_args(根据https://www.relishapp.com/rspec/rspec-expectations/v/3-0/docs/built-in-matchers/yield-matchers)。其他匹配器仅用于单次屈服。

但如果屈服的顺序与指定的顺序不同,yield_successive_args 会失败。

是否有任何方法或好的解决方法可以测试以任何顺序产生的迭代器?

类似于以下内容:

expect { |b| array.each(&b) }.to yield_multiple_args_in_any_order(1, 2, 3)

【问题讨论】:

  • 我添加了一个功能请求,请随时提出比yield_multiple_args更好的名称:github.com/rspec/rspec-expectations/issues/595
  • 你也可以在这里提供迭代器代码吗?
  • 我正在寻找任何迭代器的通用解决方案,唯一重要的是它以任何顺序产生所有参数。

标签: ruby-on-rails ruby rspec iterator yield


【解决方案1】:

这可以在纯 Ruby 中使用数组的集合交集来解决:

array1 = [3, 2, 4]
array2 = [4, 3, 2]
expect(array1).to eq (array1 & array2)

# for an enumerator:
enumerator = array1.each
expect(enumerator.to_a).to eq (enumerator.to_a & array2)

交集 (&) 将返回两个集合中都存在的项目,保持第一个参数的顺序。

【讨论】:

  • array1 = [3, 2, 4]; array2 = [4, 3, 2, 1] 怎么样?我在这里建议match_array 匹配器在比较数组时更合适。
  • 当存在具有方法 each 的迭代器并且您将 iterator.to_enum.to_amatch_array 匹配器一起使用时,这可能会导致准正确答案。但问题是关于任何迭代器方法的产生,而不是提供正确的to_enum 方法。
【解决方案2】:

这是我为这个问题提出的匹配器,它相当简单,并且应该以很高的效率工作。

require 'set'

RSpec::Matchers.define :yield_in_any_order do |*values|
  expected_yields = Set[*values]
  actual_yields = Set[]

  match do |blk|
    blk[->(x){ actual_yields << x }]    # ***
    expected_yields == actual_yields    # ***
  end

  failure_message do |actual|
    "expected to receive #{surface_descriptions_in expected_yields} "\
    "but #{surface_descriptions_in actual_yields} were yielded."
  end

  failure_message_when_negated do |actual|
    "expected not to have all of "\
    "#{surface_descriptions_in expected_yields} yielded."
  end

  def supports_block_expectations?
    true
  end
end

我用# *** 突出显示了包含大部分重要逻辑的行。这是一个非常简单的实现。

用法

只需将它放在spec/support/matchers/ 下的文件中,并确保您在需要它的规范中需要它。大多数时候,人们只是像这样添加一行:

Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}

到他们的spec_helper.rb,但如果您有很多支持文件,并且并非所有地方都需要它们,这可能会有点多,所以您可能只想在使用它的地方包含它。

然后,在规范本身中,用法与任何其他产生匹配器一样:

class Iterator
  def custom_iterator
    (1..10).to_a.shuffle.each { |x| yield x }
  end
end

describe "Matcher" do
  it "works" do
    iter = Iterator.new
    expect { |b| iter.custom_iterator(&b) }.to yield_in_any_order(*(1..10))
  end
end

【讨论】:

猜你喜欢
  • 2023-02-21
  • 2017-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
  • 2022-01-18
  • 1970-01-01
相关资源
最近更新 更多