MyObject
クラスの簡単なJUnitテストを書いています。
MyObject
は、Stringの可変引数をとる静的ファクトリメソッドから作成できます。
MyObject.ofComponents("Uno", "Dos", "Tres");
MyObject
が存在する間、クライアントは、.getComponents()
メソッドを使用して、作成されたパラメーターをList <E>の形式で検査できます。 。
myObject.ofComponents(); // -> List<String>: { "Uno", "Dos", "Tres" }
言い換えると、MyObject
は、それをもたらしたパラメーターのリストを記憶し、公開します。この契約の詳細:
getComponents
の順序は、オブジェクト作成用に選択された順序と同じですnull
の動作は未定義です(他のコードはnull
がファクトリーに到達しないことを保証します)StringのリストからMyObject
を作成し、.getComponents()
を介して同じリストを返すことができるかどうかを確認する簡単なテストを書いています。 私はすぐにこれを行いますが、これは現実的なコードパスの距離で発生するはずです。
ここで私の試み:
List<String> argumentComponents = Lists.newArrayList("One", "Two", "Three");
List<String> returnedComponents =
MyObject.ofComponents(
argumentComponents.toArray(new String[argumentComponents.size()]))
.getComponents();
assertTrue(Iterables.elementsEqual(argumentComponents, returnedComponents));
Iterables.elementsEqual()
は、ビルドパスにライブラリがある場合、これら2つのリストを比較するのに最適な方法ですか? これは私が苦しんでいることです。 Iterable <E> ..を超えるこのヘルパーメソッドを使用して、サイズを確認し、.equals()
..またはインターネット検索が示唆する他のメソッドの実行を繰り返す必要がありますか?単体テストのリストを比較する標準的な方法は何ですか?障害が発生した場合にはるかに優れた出力が得られるため、Hamcrestの使用を好みます
Assert.assertThat(listUnderTest,
IsIterableContainingInOrder.contains(expectedList.toArray()));
報告する代わりに
expected true, got false
報告します
expected List containing "1, 2, 3, ..." got list containing "4, 6, 2, ..."
IsIterableContainingInOrder.contain
Javadocによると:
検査済みのIterableを1回パスすると一連のアイテムが生成され、それぞれが指定されたアイテムの対応するアイテムに論理的に等しい場合に一致するIterablesのマッチャーを作成します。正の一致の場合、検査されるイテレート可能オブジェクトは、指定されたアイテムの数と同じ長さでなければなりません
したがって、listUnderTest
には同じ数の要素が必要であり、各要素は期待される値と順番に一致する必要があります。
単純にList#equals
を使用しないのはなぜですか?
assertEquals(argumentComponents, imapPathComponents);
2つのリストは、同じ要素が同じ順序で含まれている場合に等しいと定義されます。
List実装のequals()メソッドは要素ごとの比較を行う必要があるため、
assertEquals(argumentComponents, returnedComponents);
ずっと簡単です。
org.junit.Assert.assertEquals()
とorg.junit.Assert.assertArrayEquals()
は仕事をします。
次の質問を回避するには:順序を無視する場合は、設定するすべての要素を配置してから比較します:Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))
ただし、重複を無視するだけで順序を保持する場合は、LinkedHashSet
でリストをラップします。
さらに別のヒント。トリックAssert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))
は、比較が失敗するまで正常に機能します。この場合、セット内の順序がほとんど予測できないため(少なくとも複雑なオブジェクトの場合)、混乱を招く可能性のあるセットの文字列表現に対するエラーメッセージが表示されます。そのため、私が見つけたトリックは、HashSet
の代わりにソートされたセットでコレクションをラップすることです。カスタムコンパレータでTreeSet
を使用できます。
優れたコードの読みやすさのために、 Fest Assertions には assertingリスト の素晴らしいサポートがあります
したがって、この場合、次のようになります。
Assertions.assertThat(returnedComponents).containsExactly("One", "Two", "Three");
または、期待されるリストを配列に作成しますが、上記のアプローチの方がわかりやすいので好まれます。
Assertions.assertThat(returnedComponents).containsExactly(argumentComponents.toArray());
assertTrue()/ assertFalse():返されるブール結果をアサートするためだけに使用します
assertTrue(Iterables.elementsEqual(argumentComponents、requestedComponents));
テスト対象のメソッドがboolean
値を返すため、Assert.assertTrue()
またはAssert.assertFalse()
を使用します。
メソッドは、期待される要素を含むList
などの特定のものを返すため、このようにassertTrue()
でアサートします:Assert.assertTrue(myActualList.containsAll(myExpectedList)
はアンチパターンです。
アサーションを簡単に記述できますが、テストが失敗すると、テストランナーが次のようなことを言うだけなので、デバッグも難しくなります。
true
が期待されますが、実際はfalse
です
JUnit4のAssert.assertEquals(Object, Object)
またはJUnit 5のAssertions.assertIterableEquals(Iterable, Iterable)
:equals()
とtoString()
の両方が、比較されるオブジェクトのクラス(および深く)でオーバーライドされる場合にのみ使用する
アサーションの等価性テストはequals()
に依存しており、テスト失敗メッセージは比較されたオブジェクトのtoString()
に依存しているため重要です。String
はequals()
とtoString()
の両方をオーバーライドするため、assertEquals(Object,Object)
でList<String>
をアサートすることは完全に有効です。また、この問題については、JUnitを使用したテストでアサーションを簡単にするだけでなく、オブジェクトの同等性の観点から理にかなっているため、クラスでequals()
をオーバーライドする必要があります。
アサーションを簡単にするために、他の方法があります(答えの次のポイントで確認できます)。
グアバはユニットテストアサーションを実行/構築する方法ですか?
Google Guava Iterables.elementsEqual()は、これらの2つのリストを比較するためにビルドパスにライブラリがある場合、最良の方法ですか?
いいえそうではありません。 Guavaは、単体テストアサーションを記述するためのライブラリではありません。
ユニットテストのほとんど(私が考えるすべて)を書くのにそれは必要ありません。
単体テストのリストを比較する標準的な方法は何ですか?
良い習慣として、アサーション/マッチャーライブラリを優先します。
特定のアサーションを実行するようにJUnitを奨励することはできません。これにより、提供される機能が非常に少なく制限されているためです。
要素の順序を許可したい場合もあれば、予想される要素が実際の要素と一致するようにしたい場合もあります...
そのため、HamcrestやAssertJなどの単体テストアサーション/マッチャーライブラリを使用するのが正しい方法です。
実際の答えはHamcrestソリューションを提供します。 AssertJ ソリューションです。
org.assertj.core.api.ListAssert.containsExactly()
は必要なものです。実際のグループが指定された値を正確に含み、他に何も記載されていないことを確認します:
実際のグループに、指定された値が正確に含まれていることを確認します。
テストは次のようになります。
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@Test
void ofComponent_AssertJ() throws Exception {
MyObject myObject = MyObject.ofComponents("One", "Two", "Three");
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
}
AssertJの良い点は、予想どおりList
を宣言する必要がないことです。これにより、アサーションがよりストレートになり、コードが読みやすくなります。
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
そして、テストが失敗した場合:
// Fail : Three was not expected
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two");
次のような非常に明確なメッセージが表示されます。
Java.lang.AssertionError:
期待:
<["One"、 "Two"、 "Three"]>
正確に(そして同じ順序で)含めるには:
<["One"、 "Two"]>
しかし、いくつかの要素は予期されていませんでした:
<["Three"]>
アサーション/マッチャーライブラリは必須です。これらは本当にさらに進むからです
MyObject
はString
sではなくFoo
sインスタンスを格納するとします:
public class MyFooObject {
private List<Foo> values;
@SafeVarargs
public static MyFooObject ofComponents(Foo... values) {
// ...
}
public List<Foo> getComponents(){
return new ArrayList<>(values);
}
}
それは非常に一般的なニーズです。 AssertJを使用しても、アサーションの記述は簡単です。要素のクラスがequals()/hashCode()
をオーバーライドしない場合でも、リストの内容が等しいと断言できますが、JUnitの方法ではそうする必要があります。
import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;
@Test
void ofComponent() throws Exception {
MyFooObject myObject = MyFooObject.ofComponents(new Foo(1, "One"), new Foo(2, "Two"), new Foo(3, "Three"));
Assertions.assertThat(myObject.getComponents())
.extracting(Foo::getId, Foo::getName)
.containsExactly(Tuple(1, "One"),
Tuple(2, "Two"),
Tuple(3, "Three"));
}
Iterables.elementsEqual
が最良の選択かどうかについての私の答え:Iterables.elementsEqual
は2 List
sを比較するのに十分です。
Iterables.elementsEqual
はより一般的なシナリオで使用され、より一般的なタイプIterable
を受け入れます。つまり、List
とSet
を比較することもできます。 (順序を繰り返すことで、重要です)
確かにArrayList
とLinkedList
を定義することは非常に適切であり、直接equalsを呼び出すことができます。十分に定義されていないリストを使用する場合は、Iterables.elementsEqual
が最適です。 1つのことに注意してください:Iterables.elementsEqual
はnull
を受け入れません
リストを配列に変換するには:Iterables.toArray
が使いやすいです。
単体テストの場合、テストケースに空のリストを追加することをお勧めします。