私はミニテスト仕様を持っています:
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
ただし、これは失敗します。
Expected: [610897332, 251689721]
Actual: [251689721, 610897332]
私はそれらの両方を注文することができましたが、それは混乱を追加します:
json.map{|d| d["id"]}.sort.must_equal Database.all.pluck(:id).sort
現状では、map{}
はすでにテストとは多少無関係であり、雑然としています。これ以上追加したくないのですが。
enumerator1
のすべての項目がすべてenumerator2
にあるかどうかをテストするためのアサーションまたはヘルパーはありますか?
TL; DRこれをチェックする最も直接的な方法は、配列が等しいかどうかをチェックする前に配列をソートすることです。
json.map{|d| d["id"]}.sort.must_equal Database.all.pluck(:id).sort
まだここ?はい。配列内の要素の比較について話しましょう。
現状では、map {}はすでにテストとは多少無関係であり、雑然としています。これ以上追加したくないのですが。
まあ、それは問題の一部です。 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
テストではコード行が増えますが、意図をより適切に伝えます。それでも、あなたの「無関係」と「雑然」という言葉が私の心に響き渡るのが聞こえます。私はあなたがこの解決策を気に入らないに違いない。 「大変な作業です!」そして、「なぜ[〜#〜] i [〜#〜]がこれに責任を負わなければならないのですか?」オーケーオーケーより多くのオプションがあります。よりスマートなアサーションはどうですか?
RSpecには match_array
という名前の素敵な小さなマッチャーがあります。これはあなたが探していることをほぼ実行します。 配列をソートして比較します 一致しない場合はNiceメッセージを出力します。同様のことができます。
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
「しかし、それは主張であり、期待ではありません!」ええ、私は知っています。リラックス。 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
これで、アサーションを任意のテストで使用できるようになり、すべてのオブジェクトで期待値を利用できるようになります。
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
MiniTest Rails Shouldaには assert_same_elements
アサーション 、これ:
2つの配列に同じ要素が同じ回数含まれていることを表明します。基本的に==ですが、順序付けされていません。
assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
RSpecにはmatch_array
順序に関係なく2つの配列のマッチングを行うマッチャー。 Minitestで同様のカスタムマッチャーを作成するには、次の手順を実行できます。
module MiniTest::Assertions
class MatchEnumerator
def initialize(expected, actual)
@expected = expected
@actual = actual
end
def match()
return result, message
end
def result()
return false unless @actual.respond_to? :to_a
@extra_items = difference_between_enumerators(@actual, @expected)
@missing_items = difference_between_enumerators(@expected, @actual)
@extra_items.empty? & @missing_items.empty?
end
def message()
if @actual.respond_to? :to_a
message = "expected collection contained: #{safe_sort(@expected).inspect}\n"
message += "actual collection contained: #{safe_sort(@actual).inspect}\n"
message += "the missing elements were: #{safe_sort(@missing_items).inspect}\n" unless @missing_items.empty?
message += "the extra elements were: #{safe_sort(@extra_items).inspect}\n" unless @extra_items.empty?
else
message = "expected an array, actual collection was #{@actual.inspect}"
end
message
end
private
def safe_sort(array)
array.sort rescue array
end
def difference_between_enumerators(array_1, array_2)
difference = array_1.to_a.dup
array_2.to_a.each do |element|
if index = difference.index(element)
difference.delete_at(index)
end
end
difference
end
end # MatchEnumerator
def assert_match_enumerator(expected, actual)
result, message = MatchEnumerator.new(expected, actual).match
assert result, message
end
end # MiniTest::Assertions
Enumerator.infect_an_assertion :assert_match_enumerator, :assert_match_enumerator
次のテストで、このカスタムマッチャーの動作を確認できます。
describe "must_match_enumerator" do
it{ [1, 2, 3].map.must_match_enumerator [1, 2, 3].map }
it{ [1, 2, 3].map.must_match_enumerator [1, 3, 2].map }
it{ [1, 2, 3].map.must_match_enumerator [2, 1, 3].map }
it{ [1, 2, 3].map.must_match_enumerator [2, 3, 1].map }
it{ [1, 2, 3].map.must_match_enumerator [3, 1, 2].map }
it{ [1, 2, 3].map.must_match_enumerator [3, 2, 1].map }
# deliberate failures
it{ [1, 2, 3].map.must_match_enumerator [1, 2, 1].map }
end
したがって、このカスタムマッチャーを使用すると、テストを次のように書き直すことができます。
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_match_enumerator Database.all.pluck(:id)
end
array substraction in Ruby次のように使用できます:
assert_empty(["A", "B"] - ["B", "A"])
ただし、次の点に注意してください。["A"、 "B"]-["B"、 "A"] == [][〜#〜] but [〜#〜 ]["A"、 "B"、 "B"]-["B"、 "A"] == []
したがって、この手法は、固有の値がある場合にのみ使用してください。
1つのオプションは、繰り返しが問題にならない場合にセットを使用することです(標準のRuby)
require 'set'
assert_equals [1,2,3].to_set, [3,2,1].to_set
それ以外の場合は、独自のアサートメソッドを記述します( 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!
または、 shoulda gemを直接追加してください。
パフォーマンスが重要ではないテストシナリオでは、反復と assert_include
を使用できます。次に例を示します。
test_result_items.each { |item| assert_include(expected_items, item) }
ここで、test_result_items
はテスト対象のコードの結果を含む配列であり、expected_items
は予想される項目を(任意の順序で)含む配列です。
すべてのアイテムが存在する(そして余分なアイテムが存在しない)ことを確認するには、上記を配列の長さのチェックと組み合わせます。
assert_equal expected_items.length, test_result_items.length
これは、アイテムが一意である場合にのみ2つの配列が等しいことを確立することに注意してください。 (test_result_items
と['a', 'a', 'a']
には、実際にはexpected_items
の['a', 'b', 'c']
に存在するアイテムしか含まれていないためです。)