JUnitの2つのリストが等しいことをアサートする


164

JUnitテストケースのリスト間で等価アサーションを作成するにはどうすればよいですか?平等はリストの内容の間にあるべきです。

例えば:

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four"); 

// numbers should be equal to numbers2
//numbers should not be equal to numbers3

5
assertArrayEquals最近は好きです。と組み合わせて使用​​しList#toArrayます。
Thibstars、2018

@Thibstars-私はそれを答えとして賛成したい。
dfrankow 2018

2
assertArrayEqualsリストから配列を取得する必要があります。assertIterableEquals
ThetaSinner

assertIterableEqualsjUnit5 @ThetaSinnerで利用可能
iec2011007

回答:


168

これは数年前に尋ねられたことに気づきました。おそらくこの機能は当時はなかったでしょう。しかし今、これを行うのは簡単です:

@Test
public void test_array_pass()
{
  List<String> actual = Arrays.asList("fee", "fi", "foe");
  List<String> expected = Arrays.asList("fee", "fi", "foe");

  assertThat(actual, is(expected));
  assertThat(actual, is(not(expected)));
}

hamcrestでインストールされたJunitの最新バージョンがある場合は、次のインポートを追加するだけです。

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T、org.hamcrest.Matcher)

http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html

http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html


3
System.out.println(actual == expected);falseを返しますが、System.out.println(actual.equals(expected));trueを返します。
ナマズ、2014年

@キャットフィッシュええ、それは紛らわしいですよね。マッチャーがの.equals(..)代わりに使用していることを示していたと思います==か?
djeikyb 14年

2
それはassertEqualsよりどのように優れていますか?
Raedwald

1
@Raedwaldアサーションが失敗したときの出力。後で戻って違いを編集してみます。hamcrestマッチャーは、詳細な失敗メッセージを生成できます。junitがassertEqualsを単純に同様のオーバーロードでオーバーロードする可能性があります。しかし、ほとんどのjunitはコアユニットテスト機能を提供し、hamcrestは便利なオブジェクト差分記述ライブラリを提供します。
djeikyb 2018年

29

文字列に変換して比較しないでください。これはパフォーマンスに良くありません。junitのCorematchers内には、このためのマッチャーがあります=>hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

これは、リストの要素をチェックするために私が知っているより良い方法です。


2
選択された回答である必要があります。1つの注意事項があります。リストには、必要なもの以外に項目がないことも確認する必要があります。多分使用します:Assert.assertEquals(4,yourList.size());
yoni

または1行で:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602 2018年

3
「これはパフォーマンスに良くありません。」単体テストを作成する際に、パフォーマンスをどの程度考慮に入れるべきかわかりません...しかし、toString()バージョンを介して文字列を比較することは最善の方法ではありません。
walen

21

これはレガシー回答であり、JUnit 4.3以下に適しています。JUnitの最新バージョンでは、assertThatメソッドに読み取り可能な障害メッセージが組み込まれています。可能であれば、この質問に対して他の回答を優先してください。

List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
            "\n  'a'        = "+a+
            "\n  'expected' = "+expected, 
            expected.equals(a));

記録のために、@ Paulがこの回答への彼のコメントで述べたように、2つListのは同等です。

指定されたオブジェクトもリストである場合に限り、両方のリストは同じサイズであり、2つのリスト内の対応する要素のペアはすべて等しくなります。(2つの要素e1とのe2場合は等しい(e1==null ? e2==null : e1.equals(e2))。)つまり、2つのリストは、同じ要素が同じ順序で含まれている場合に等しいと定義されます。この定義により、equalsメソッドがListインターフェースの異なる実装間で適切に機能することが保証されます。

インターフェースJavaDocsをList参照してください


1
つまり、expected.equals(a)は、リストが保持しているオブジェクトのアサートを処理しますか?
カマル

1
リストjavadocから:指定されたオブジェクトとこのリストが等しいかどうかを比較します。指定されたオブジェクトもリストであり、両方のリストのサイズが同じで、2つのリスト内の対応する要素のペアがすべて等しい場合にのみ、trueを返します。((e1 == null?e2 == null:e1.equals(e2))の場合、2つの要素e1とe2は等しくなります。つまり、同じ順序で同じ要素が含まれている場合、2つのリストは等しいと定義されます。 。この定義により、equalsメソッドがListインターフェースのさまざまな実装にわたって適切に機能することが保証されます。
ポールマッケンジー

1
この悲しいかな、有用なエラーメッセージは提供されません。ループを実行するユーティリティクラスを記述して、どの要素が異なるかを確認できるようにした方がいいと思いました。
マイケルロイドリーmlk 2010

@mlkかもしれませんが、私はそのようなもののためのカスタムユーティリティメソッドを書くのをためらっています。今編集したエラーメッセージはどうなりますか?
Bart Kiers、2013

@mlk各要素をテストするループを記述して、何が違うのかを正確に理解する方がよいことに同意します。リストにあるオブジェクトのタイプによって異なります。彼らは文字列である場合、単に「=」+問題ないはずですが、それらは複雑なオブジェクト(他のリスト、または良いのtoStringの実装を持っていないもの)であれば、個別にそれらをテストする方が簡単かもしれないと言います
マットN

20

assertEquals(Object, Object)JUnit4 / JUnitの5から又は assertThat(actual, is(expected));Hamcrestからのみの両方として機能する他の回答で提案equals()toString()比較オブジェクトのクラス(深く)するためにオーバーライドされます。

アサーションの等価テストが依存しequals()、テスト失敗メッセージtoString()が比較対象のオブジェクトに依存するため、これは重要です。
用ビルトインのようなクラスStringIntegerおよびその...のためにこれらのオーバーライドとして問題はないの両方equals()toString()。したがって、アサートList<String>またはList<Integer>とともに使用 することは完全に有効ですassertEquals(Object,Object)
この問題についてequals()は、JUnitを使用したテストでアサーションを簡単にするだけでなく、オブジェクトの等価性の観点からも意味があるため、クラスでオーバーライドする必要があります。
アサーションを簡単にするには、他の方法があります。
良い方法として、私はアサーション/マッチャーライブラリを優先します。

これがAssertJソリューションです。

org.assertj.core.api.ListAssert.containsExactly() 必要なものです。実際のグループには、javadocに記載されている順序で、指定された値のみが含まれ、他には何も含まれていないことを確認します。

Foo要素を追加し、それを取得できるクラスを想定します。
その単体テストFooは、2つのリストが同じ内容であると断言します。

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception { 
   Foo foo = new Foo();
   foo.add("One", "Two", "Three");
   Assertions.assertThat(foo.getElements())
             .containsExactly("One", "Two", "Three");
}

AssertJの良い点は、List期待どおりにを宣言する必要がないことです。これにより、アサーションがより簡潔になり、コードが読みやすくなります。

Assertions.assertThat(foo.getElements())
         .containsExactly("One", "Two", "Three");

しかし、アサーション/マッチャーライブラリーは本当に必要になるため、必須です。
今でFooStringsではなくBarsインスタンスを格納するとします。
それは非常に一般的なニーズです。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 add() throws Exception {
    Foo foo = new Foo();
    foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
    Assertions.assertThat(foo.getElements())
              .extracting(Bar::getId, Bar::getName)
              .containsExactly(tuple(1, "One"),
                               tuple(2, "Two"),
                               tuple(3, "Three"));
}

7

要素の順序を気にしない場合はListAssert.assertEquals、junit-addons をお勧めします。

リンク:http : //junit-addons.sourceforge.net/

怠惰なMavenユーザーの場合:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

7
注:要素の順序を気にしない場合は、リストではなくセットまたはコレクションを使用する必要があります。
Barett

11
同意する。このライブラリは粗雑です。なぜいったいListAssert.assertEquals()がデフォルトでオーダーレスになるのでしょうか?
ライアン14

5

jUnitassertEqualsを使用できます。

import org.junit.Assert;   
import org.junit.Test;

    @Test
    public void test_array_pass()
    {
        List<String> actual = Arrays.asList("fee", "fi", "foe");
        List<String> expected = Arrays.asList("fee", "fi", "foe");
        Assert.assertEquals(actual,expected);
    }

要素の順序が異なる場合、エラーが返されます。

モデルオブジェクトリストをアサートする場合は、特定のモデルのequalsメソッドをオーバーライドする必要があります。

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj instanceof ModelName) {
            ModelName other = (ModelName) obj;
            return this.getItem().equals(other.getItem()) ;
        }
        return false;
    }

4

配列リストを作成したくない場合は、これも試すことができます

@Test
public void test_array_pass()
{
  List<String> list = Arrays.asList("fee", "fi", "foe");
  Strint listToString = list.toString();
  Assert.assertTrue(listToString.contains("[fee, fi, foe]"));   // passes  
}

3
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));

1

ホイールを再発明しないでください!

これを行うGoogle Codeライブラリがあります:Hamcrest

[Hamcrest]マッチャーオブジェクト(制約または述語とも呼ばれます)のライブラリを提供し、「一致」ルールを宣言的に定義して、他のフレームワークで使用できるようにします。典型的なシナリオには、テストフレームワーク、モックライブラリ、UI検証ルールが含まれます。


0

私はすでにこの問題を解決するための多くのオプションがあることを知っていますが、いずれかのオーダーで2つのリストアサートするために次のようにしたいと思います

assertTrue(result.containsAll(expected) && expected.containsAll(result))

注文が一致しない場合、これは失敗しませんか?
iec2011007

0

assertEquals(expected, result);私のために働く。この関数は2つのオブジェクトを取得するため、何でも渡すことができます。

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

-1

上記のすべての回答がオブジェクトの2つのリストを比較するための正確な解決策を提供しているとは思いません。上記のアプローチのほとんどは、比較の制限のみに従う場合に役立ちます-サイズ比較-参照比較

しかし、オブジェクトのサイズが同じでオブジェクトレベルのデータが異なる場合、この比較アプローチは役に立ちません。

次のアプローチは、ユーザー定義オブジェクトでのequalsおよびhashcodeメソッドのオーバーライドで完全に機能すると思います。

等しい値とハッシュコードを上書きするためにXstream lib を使用しましたが、勝ったロジック/比較によっても等しい値とハッシュコードを上書きできます。

あなたの参照のための例はここにあります

    import com.thoughtworks.xstream.XStream;

    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.List;

    class TestClass {
      private String name;
      private String id;

      public void setName(String value) {
        this.name = value;
      }

      public String getName() {
        return this.name;
      }

      public String getId() {
        return id;
      }

      public void setId(String id) {
        this.id = id;
      }

      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      @Override
      public boolean equals(Object o) {
        XStream xstream = new XStream();
        String oxml = xstream.toXML(o);
        String myxml = xstream.toXML(this);

        return myxml.equals(oxml);
      }

      /**
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        XStream xstream = new XStream();
        String myxml = xstream.toXML(this);
        return myxml.hashCode();
      }
    }

    public class XstreamCompareTest {
      public static void main(String[] args) throws ParseException {
      checkObjectEquals();
}

      private static void checkObjectEquals() {
        List<TestClass> testList1 = new ArrayList<TestClass>();
        TestClass tObj1 = new TestClass();
        tObj1.setId("test3");
        tObj1.setName("testname3");
        testList1.add(tObj1);

        TestClass tObj2 = new TestClass();
        tObj2.setId("test2");
        tObj2.setName("testname2");
        testList1.add(tObj2);

        testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        List<TestClass> testList2 = new ArrayList<TestClass>();
        TestClass tObj3 = new TestClass();
        tObj3.setId("test3");
        tObj3.setName("testname3");
        testList2.add(tObj3);

        TestClass tObj4 = new TestClass();
        tObj4.setId("test2");
        tObj4.setName("testname2");
        testList2.add(tObj4);

        testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        if (isNotMatch(testList1, testList2)) {
          System.out.println("The list are not matched");
        } else {
          System.out.println("The list are matched");
        }

      }

      private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
        return clist1.size() != clist2.size() || !clist1.equals(clist2);
      }
    }

最も重要なことは、オブジェクトの等しいチェックにフィールドを含めない場合は、アノテーション(@XStreamOmitField)でフィールドを無視できることです。設定するこのような多くの注釈があるので、このlibの注釈について深く見てください。

この答えは、オブジェクトの2つのリストを比較するための正しいアプローチを特定するための時間を節約できると確信しています:)。これに関して何か問題があればコメントしてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.