Menyatakan sama dengan antara 2 Daftar di Junit


164

Bagaimana saya bisa membuat pernyataan kesetaraan antara daftar dalam kasus uji JUnit ? Kesetaraan harus berada di antara isi daftar.

Sebagai contoh:

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
Saya suka menggunakan assertArrayEqualssaat ini. Gunakan dalam kombinasi dengan List#toArray.
Thibstars

@Thibstars - Saya menjawab itu sebagai jawaban.
dfrankow

2
assertArrayEqualsmengharuskan Anda untuk mendapatkan array dari daftar Anda. Anda dapat beroperasi secara langsung pada daftar menggunakanassertIterableEquals
ThetaSinner

assertIterableEqualstersedia untuk jUnit5 @ThetaSinner
iec2011007

Jawaban:


168

Saya menyadari ini ditanya beberapa tahun yang lalu, mungkin fitur ini belum ada. Tapi sekarang, mudah untuk melakukan ini:

@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)));
}

Jika Anda memiliki Junit versi terbaru yang terinstal dengan hamcrest, cukup tambahkan impor ini:

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);akan mengembalikan false, tetapi System.out.println(actual.equals(expected));akan mengembalikan true.
Lele

@ Kucing ya, itu membingungkan bukan. Saya pikir saya menunjukkan bahwa pencocokan menggunakan .equals(..)bukan ==?
djeikyb

2
Bagaimana itu lebih baik daripada assertEquals?
Raedwald

1
@Redwald output ketika pernyataan gagal. saya akan mencoba kembali lagi nanti untuk mengedit perbedaannya. pencocokan hamcrest dapat menghasilkan pesan kegagalan terperinci. itu mungkin bagi junit untuk hanya membebani assertEquals dengan kebaikan yang sama. tetapi sebagian besar junit menyediakan fitur-fitur pengujian unit inti, dan hamcrest menyediakan pustaka pendeskripsikan perbedaan objek yang bagus untuk dimiliki.
djeikyb

29

Jangan bertransformasi menjadi string dan bandingkan. Ini tidak baik untuk kinerja. Di Junit, di dalam Corematchers, ada korek api untuk ini =>hasItems

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

Ini adalah cara terbaik yang saya tahu untuk mengecek elemen dalam daftar.


2
Seharusnya jawaban yang dipilih, dengan satu catatan: Anda juga perlu memverifikasi bahwa tidak ada lagi item dalam daftar selain yang Anda inginkan. Mungkin gunakan:Assert.assertEquals(4,yourList.size());
yoni

atau dengan satu baris:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602

3
"Ini tidak baik untuk kinerja." → Saya tidak yakin sampai tingkat mana seseorang harus memperhitungkan kinerja saat menulis tes unit ... Tetapi tentu saja, membandingkan string melalui toString()versi mereka bukanlah cara terbaik.
walen

21

Ini adalah jawaban lama, cocok untuk JUnit 4.3 dan di bawah. Versi modern JUnit mencakup pesan kegagalan yang dapat dibaca yang dapat dibaca di dalam metode assertThat. Pilih jawaban lain untuk pertanyaan ini, jika memungkinkan.

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));

Sebagai catatan, seperti @ Paul disebutkan dalam komentarnya untuk jawaban ini, dua Lists sama:

jika dan hanya jika objek yang ditentukan juga merupakan daftar, kedua daftar memiliki ukuran yang sama, dan semua pasangan elemen yang sesuai dalam kedua daftar adalah sama. (Dua elemen e1dan e2sama jika (e1==null ? e2==null : e1.equals(e2)).) Dengan kata lain, dua daftar didefinisikan sama jika mengandung elemen yang sama dalam urutan yang sama. Definisi ini memastikan bahwa metode yang sama berfungsi dengan baik di seluruh implementasi Listantarmuka yang berbeda.

Lihat JavaDocs dari Listantarmuka .


1
Jadi maksud Anda diharapkan. Sama dengan (a) akan berusaha menegaskan objek yang dipegang daftar?
Kamal

1
Dari List javadoc: Membandingkan objek yang ditentukan dengan daftar ini untuk persamaan. Mengembalikan nilai true jika dan hanya jika objek yang ditentukan juga merupakan daftar, kedua daftar memiliki ukuran yang sama, dan semua pasangan elemen yang sesuai dalam kedua daftar adalah sama. (Dua elemen e1 dan e2 sama jika (e1 == null? E2 == null: e1.equals (e2)).) Dengan kata lain, dua daftar didefinisikan sama jika mengandung elemen yang sama dalam urutan yang sama . Definisi ini memastikan bahwa metode yang sama berfungsi dengan baik di seluruh implementasi berbeda dari antarmuka Daftar.
Paul McKenzie

1
Sayangnya ini memberikan pesan kesalahan yang kurang bermanfaat. Saya telah menemukan lebih baik untuk menulis kelas utilitas yang melakukan perulangan sehingga Anda dapat melihat elemen mana yang berbeda.
Michael Lloyd Lee mlk

@mlk, mungkin, tapi saya ragu untuk menulis metode utilitas khusus untuk hal semacam itu. Bagaimana dengan pesan kesalahan yang saya edit tadi?
Bart Kiers

@mlk Saya setuju bahwa mungkin lebih baik untuk menulis loop untuk menguji setiap elemen sehingga Anda tahu persis apa yang berbeda. Itu tergantung pada jenis objek apa yang ada dalam daftar. Jika mereka adalah Strings, maka katakan saja "a =" + a harus baik-baik saja, tetapi jika mereka adalah objek yang kompleks (daftar lain, atau sesuatu yang tidak memiliki implementasi toString yang baik), mungkin lebih mudah untuk mengujinya secara individual
Matt N

20

assertEquals(Object, Object)dari JUnit4 / JUnit 5 atau assertThat(actual, is(expected));dari Hamcrest yang diusulkan dalam jawaban lain hanya akan berfungsi karena keduanya equals()dan toString()ditimpa untuk kelas (dan sangat) dari objek yang dibandingkan.

Itu penting karena tes kesetaraan dalam pernyataan bergantung equals()dan pesan kegagalan pengujian bergantung pada toString()objek yang dibandingkan.
Untuk kelas bawaan seperti String, Integerdan untuk ... tidak ada masalah karena ini menimpa keduanya equals()dan toString(). Jadi sangat valid untuk menegaskan List<String>atau List<Integer>dengan assertEquals(Object,Object).
Dan tentang masalah ini: Anda harus menimpa equals()di kelas karena masuk akal dalam hal kesetaraan objek, tidak hanya untuk membuat pernyataan lebih mudah dalam ujian dengan JUnit.
Untuk membuat pernyataan lebih mudah, Anda memiliki cara lain.
Sebagai praktik yang baik saya mendukung perpustakaan pernyataan / pencocokan.

Berikut ini adalah solusi AssertJ .

org.assertj.core.api.ListAssert.containsExactly() adalah apa yang Anda butuhkan: itu memverifikasi bahwa grup yang sebenarnya berisi persis nilai yang diberikan dan tidak ada yang lain, agar seperti yang dinyatakan dalam javadoc.

Misalkan Fookelas tempat Anda menambahkan elemen dan di mana Anda bisa mendapatkannya.
Tes unit Fooyang menyatakan bahwa kedua daftar memiliki konten yang sama dapat terlihat seperti:

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");
}

Poin bagus AssertJ adalah bahwa menyatakan Listseperti yang diharapkan tidak perlu: itu membuat pernyataan menjadi lebih lurus dan kode lebih mudah dibaca:

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

Tapi perpustakaan Penegasan / pencocokan adalah suatu keharusan karena ini akan sangat jauh.
Misalkan sekarang yang Foo tidak menyimpan instance Stringtetapi Bars.
Itu adalah kebutuhan yang sangat umum. Dengan Assert, pernyataan itu masih mudah ditulis. Lebih baik Anda dapat menyatakan bahwa konten daftar sama bahkan jika kelas elemen tidak menimpa equals()/hashCode()sementara cara JUnit mengharuskan:

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

Jika Anda tidak peduli dengan urutan elemen, saya sarankan ListAssert.assertEqualsdi add-junit.

Tautan: http://junit-addons.sourceforge.net/

Untuk pengguna malas Maven:

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

7
Catatan: Jika Anda tidak peduli dengan urutan elemen, Anda harus menggunakan Set atau Koleksi, bukan Daftar.
Barett

11
Saya setuju. Perpustakaan ini kotor. Kenapa ListAssert.assertEquals () akan default ke pesanan?
Ryan

5

Anda dapat menggunakan assertEquals di junit.

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);
    }

Jika urutan elemen berbeda maka akan mengembalikan kesalahan.

Jika Anda menegaskan daftar objek model maka Anda harus mengganti metode equals dalam model tertentu.

    @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

jika Anda tidak ingin membangun daftar array, Anda dapat mencoba ini juga

@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

Jangan menemukan kembali roda!

Ada perpustakaan Google Code yang melakukan ini untuk Anda: Hamcrest

[Hamcrest] Menyediakan perpustakaan objek matcher (juga dikenal sebagai kendala atau predikat) yang memungkinkan aturan 'match' didefinisikan secara deklaratif, untuk digunakan dalam kerangka kerja lain. Skenario umum termasuk kerangka pengujian, mengejek perpustakaan dan aturan validasi UI.


0

Saya tahu sudah ada banyak opsi untuk menyelesaikan masalah ini, tetapi saya lebih suka melakukan yang berikut untuk menegaskan dua daftar di setiap oder :

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

apakah ini tidak akan gagal jika pesanan tidak cocok ??
iec2011007

0

assertEquals(expected, result);bekerja untukku. Karena fungsi ini mendapat dua objek, Anda dapat meneruskan apa saja ke sana.

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

-1

Saya tidak melakukan ini, semua jawaban di atas memberikan solusi yang tepat untuk membandingkan dua daftar Objek. Sebagian besar pendekatan di atas dapat membantu dalam mengikuti perbandingan perbandingan saja - Perbandingan ukuran - Perbandingan referensi

Tetapi jika kita memiliki daftar objek berukuran sama dan data berbeda pada tingkat objek maka pendekatan perbandingan ini tidak akan membantu.

Saya pikir pendekatan berikut ini akan bekerja dengan sempurna dengan mengesampingkan sama dan metode kode hash pada objek yang ditentukan pengguna.

Saya menggunakan Xstream lib untuk mengesampingkan equals dan hashcode tapi kita bisa mengesampingkan equals dan hashcode oleh won won logika / perbandingan juga.

Ini adalah contoh untuk referensi Anda

    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);
      }
    }

Yang paling penting adalah Anda bisa mengabaikan bidang dengan Anotasi (@XStreamOmitField) jika Anda tidak ingin menyertakan bidang apa pun pada pemeriksaan Objek yang sama. Ada banyak Anotasi seperti ini untuk dikonfigurasikan jadi lihat lebih dalam tentang anotasi lib ini.

Saya yakin jawaban ini akan menghemat waktu Anda untuk mengidentifikasi pendekatan yang benar untuk membandingkan dua daftar objek :). Berikan komentar jika Anda melihat ada masalah dalam hal ini.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.