如何断言数组的内容,对顺序无动于衷

ber*_*kes 21 ruby-on-rails minitest assertion

我有一个最小的规格:

it "fetches a list of all databases" do
  get "/v1/databases"
  json = JSON.parse(response.body)
  json.length.must_equal           Database.count
  json.map{|d| d["id"]}.must_equal Database.all.pluck(:id)
end
Run Code Online (Sandbox Code Playgroud)

但是,这失败了:

Expected: [610897332, 251689721]
  Actual: [251689721, 610897332]
Run Code Online (Sandbox Code Playgroud)

我可以同时订购它们,但这会增加混乱:

json.map{|d| d["id"]}.sort.must_equal Database.all.pluck(:id).sort
Run Code Online (Sandbox Code Playgroud)

事实上,它map{}已经与测试有点无关并且增加了混乱,我宁愿不再添加更多.

是否有断言或助手来测试是否所有物品enumerator1都在enumerator2

blo*_*age 15

TL; DR检查这个问题的最直接方法是在检查数组之前对数组进行排序.

json.map{|d| d["id"]}.sort.must_equal Database.all.pluck(:id).sort
Run Code Online (Sandbox Code Playgroud)

还在?好的.我们来谈谈比较数组中的元素.

事实上,地图{}已经与测试有点无关并且增加了混乱,我宁愿不再添加更多.

嗯,这是问题的一部分.您的JSON包含一个JSON对象数组,而调用Database.pluck将返回其他内容,可能是整数.您需要将JSON对象和查询转换为相同的数据类型.所以说这.map{}是无关紧要是不准确的,如果它感觉像是混乱,那就是因为你在断言中做了很多事情.尝试拆分该行代码并使用意图揭示名称:

sorted_json_ids = json.map{|d| d["id"]}.sort
sorted_db_ids   = Database.order(:id).pluck(:id)
sorted_json_ids.must_equal sorted_db_ids
Run Code Online (Sandbox Code Playgroud)

在测试中有更多的代码行,但更好地传达了意图.然而,我听到你的话语"无关紧要"和"混乱"在我脑海中回荡.我打赌你不喜欢这个解决方案."它的工作太多了!" 并且"为什么必须对此负责?" 好吧好吧.我们有更多选择.更智能的断言怎么样?

RSpec有一个很好的小匹配器match_array,可以满足您的需求.它对数组进行排序和比较,如果它们不匹配则打印出一条好消息.我们可以做类似的事情.

def assert_matched_arrays expected, actual
  assert_equal expected.to_ary.sort, actual.to_ary.sort
end

it "fetches a list of all databases" do
  get "/v1/databases"
  json = JSON.parse(response.body)
  assert_matched_arrays Database.pluck(:id), json.map{|d| d["id"]}
end
Run Code Online (Sandbox Code Playgroud)

"但这是一个断言而不是期望!" 是的,我知道.放松.您可以通过调用将断言转换为期望infect_an_assertion.但要做到这一点,您可能希望添加断言方法,以便可以在每个Minitest测试中使用它.所以在我的test_helper.rb文件中我添加以下内容:

module MiniTest::Assertions
  ##
  # Fails unless <tt>exp</tt> and <tt>act</tt> are both arrays and
  # contain the same elements.
  #
  #     assert_matched_arrays [3,2,1], [1,2,3]

  def assert_matched_arrays exp, act
    exp_ary = exp.to_ary
    assert_kind_of Array, exp_ary
    act_ary = act.to_ary
    assert_kind_of Array, act_ary
    assert_equal exp_ary.sort, act_ary.sort
  end
end

module MiniTest::Expectations
  ##
  # See MiniTest::Assertions#assert_matched_arrays
  #
  #     [1,2,3].must_match_array [3,2,1]
  #
  # :method: must_match_array

  infect_an_assertion :assert_matched_arrays, :must_match_array
end
Run Code Online (Sandbox Code Playgroud)

现在您的断言可用于任何测试,您的期望将在每个对象上可用.

it "fetches a list of all databases" do
  get "/v1/databases"
  json = JSON.parse(response.body)
  json.map{|d| d["id"]}.must_match_array Database.pluck(:id)
end
Run Code Online (Sandbox Code Playgroud)


小智 5

MiniTest Rails Shoulda具有一个assert_same_elements断言,该断言

断言两个数组包含相同的元素,相同的次数。本质上==,但无序。

assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
Run Code Online (Sandbox Code Playgroud)

  • 警告:您需要使用其他宝石才能正常工作,即:shoda (3认同)

est*_*ani 5

如果重复不是问题,一种选择是使用集合(标准红宝石)

 require 'set'
 assert_equals [1,2,3].to_set, [3,2,1].to_set
Run Code Online (Sandbox Code Playgroud)

其他明智的写你自己的断言方法(来自shoulda

module Minitest::Assertions
  def assert_same_elements(expected, current, msg = nil)
    assert expected_h = expected.each_with_object({}) { |e, h| h[e] ||= expected.select { |i| i == e }.size }
    assert current_h = current.each_with_object({}) { |e, h| h[e] ||= current.select { |i| i == e }.size}

    assert_equal(expected_h, current_h, msg)
  end
end

assert_same_elements [1,2,3,3], [3,2,1,3] # ok!
assert_same_elements [1,2,3,3], [3,2,1] # fails!
Run Code Online (Sandbox Code Playgroud)

或者直接添加shoulda gem 以获得更多信息。