Assert równa się 2 listom w Junit

165

Jak mogę zapewnić równość między listami w przypadku testowym JUnit ? Treść listy powinna być równa.

Na przykład:

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
Kamal
źródło
5
assertArrayEqualsObecnie lubię używać . Używaj w połączeniu z List#toArray.
Thibstars
@Thibstars - zagłosowałbym za tym jako odpowiedź.
dfrankow
2
assertArrayEqualswymaga pobrania tablic z list. Możesz działać bezpośrednio na liście za pomocąassertIterableEquals
ThetaSinner
assertIterableEqualsdostępne dla jUnit5 @ThetaSinner
iec2011007

Odpowiedzi:

170

Zdaję sobie sprawę, że pytano o to kilka lat temu, prawdopodobnie wtedy nie było tej funkcji. Ale teraz łatwo to zrobić:

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

Jeśli masz najnowszą wersję Junit zainstalowaną z hamcrestem, po prostu dodaj następujące importy:

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

djeikyb
źródło
3
System.out.println(actual == expected);zwróci wartość false, ale System.out.println(actual.equals(expected));zwróci true.
Sum,
@Catfish yeah, to nie jest mylące. Myślę, że pokazałem, że dopasowujący używa .equals(..)zamiast ==?
djeikyb
2
Jak to jest lepsze niż assertEquals?
Raedwald
1
@Raedwald dane wyjściowe, gdy potwierdzenie nie powiedzie się. spróbuję wrócić później, aby edytować różnicę. Dopasowujące hamcrest mogą generować szczegółowe komunikaty o błędach. junit może po prostu przeciążać assertEquals o podobnej dobroci. ale głównie junit zapewnia podstawowe funkcje testów jednostkowych, a hamcrest zapewnia przyjemną w użyciu bibliotekę opisującą różnice obiektów.
djeikyb
29

Nie przekształcaj w ciąg i nie porównuj. To nie jest dobre dla wydajności. W junit, wewnątrz Corematchers, znajduje się dopasowanie do this =>hasItems

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

Jest to lepszy sposób, jaki znam, do sprawdzania elementów na liście.

Andre Fonseca
źródło
2
Powinna być wybrana odpowiedź, z jedną uwagą: Musisz również sprawdzić, czy na liście nie ma więcej pozycji poza tym, co chcesz. Może użyj:Assert.assertEquals(4,yourList.size());
yoni
lub za pomocą jednej linii:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602
3
„To nie jest dobre dla wykonania”. → Nie jestem pewien, w jakim stopniu należy brać pod uwagę wydajność podczas pisania testów jednostkowych ... Ale toString()oczywiście porównywanie łańcuchów poprzez ich wersję nie jest najlepszym sposobem.
walen
21

To jest starsza odpowiedź, odpowiednia dla JUnit 4.3 i starszych. Nowoczesna wersja JUnit zawiera wbudowane czytelne komunikaty o błędach w metodzie assertThat. Jeśli to możliwe, wolę inne odpowiedzi na to pytanie.

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

Dla przypomnienia, jak @Paul wspomniał w swoim komentarzu do tej odpowiedzi, dwa Lists są równe:

wtedy i tylko wtedy, gdy określony obiekt jest również listą, obie listy mają ten sam rozmiar i wszystkie odpowiadające im pary elementów na obu listach są równe. (Dwa elementy e1i e2są równe, jeśli (e1==null ? e2==null : e1.equals(e2)).) Innymi słowy, dwie listy są zdefiniowane jako równe, jeśli zawierają te same elementy w tej samej kolejności. Ta definicja zapewnia, że ​​metoda equals działa poprawnie w różnych implementacjach Listinterfejsu.

Zobacz dokumentację JavaDocs Listinterfejsu .

Bart Kiers
źródło
1
Więc masz na myśli oczekiwanie. Quals (a) zajmie się potwierdzeniem obiektów, które zawiera lista?
Kamal
1
Z listy javadoc: porównuje określony obiekt z tą listą pod kątem równości. Zwraca wartość true wtedy i tylko wtedy, gdy określony obiekt jest również listą, obie listy mają ten sam rozmiar, a wszystkie odpowiadające im pary elementów na obu listach są równe. (Dwa elementy e1 i e2 są równe, jeśli (e1 == null? E2 == null: e1.equals (e2)). Innymi słowy, dwie listy są zdefiniowane jako równe, jeśli zawierają te same elementy w tej samej kolejności . Ta definicja zapewnia, że ​​metoda equals działa poprawnie w różnych implementacjach interfejsu List.
Paul McKenzie,
1
Niestety, ten komunikat jest mniej niż pomocny. Zauważyłem, że lepiej jest napisać klasę użytkową, która wykonuje pętlę, dzięki czemu można zobaczyć, które elementy są różne.
Michael Lloyd Lee mlk
@mlk, być może, ale waham się, czy napisać niestandardową metodę narzędzia do takich rzeczy. A co z komunikatem o błędzie, który właśnie edytowałem?
Bart Kiers,
@mlk Zgadzam się, że lepiej byłoby napisać pętlę do testowania każdego elementu, aby dokładnie wiedzieć, co jest inne. Zależy to od typów obiektów znajdujących się na liście. Jeśli są to łańcuchy, po prostu powiedz „a =” + a powinno wystarczyć, ale jeśli są to złożone obiekty (inne listy lub coś, co nie ma dobrej implementacji toString), łatwiej byłoby je przetestować indywidualnie
Matt N,
20

assertEquals(Object, Object)z JUnit4 / JUnit 5 lub assertThat(actual, is(expected));z Hamcrest zaproponowane w innych odpowiedziach będą działać tylko jako oba equals()i toString()są nadpisywane dla klas (i głęboko) porównywanych obiektów.

Ma to znaczenie, ponieważ test równości w asercji opiera się, equals()a komunikat o niepowodzeniu testu opiera się toString()na porównywanych obiektach.
Dla wbudowanych klas, takich jak String, Integeri tak za ... nie ma problemu, ponieważ te zastępują zarówno equals()i toString(). Tak więc jest to całkowicie słuszne, aby twierdzić List<String>lub List<Integer>z assertEquals(Object,Object).
A jeśli chodzi o tę sprawę: musisz nadpisać equals()w klasie, ponieważ ma to sens z punktu widzenia równości obiektów, a nie tylko po to, aby ułatwić asercje w teście z JUnit.
Aby ułatwić asercje, masz inne sposoby.
Jako dobrą praktykę preferuję biblioteki asercji / dopasowań.

Oto rozwiązanie AssertJ .

org.assertj.core.api.ListAssert.containsExactly() jest tym, czego potrzebujesz: sprawdza, czy aktualna grupa zawiera dokładnie podane wartości i nic więcej, w kolejności określonej w javadoc.

Załóżmy Fooklasę, w której dodajesz elementy i gdzie możesz to uzyskać.
Test jednostkowy, Fooktóry potwierdza, że ​​obie listy mają taką samą zawartość, mógłby wyglądać następująco:

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

Dobrą stroną AssertJ jest to, że deklarowanie a Listzgodnie z oczekiwaniami jest niepotrzebne: sprawia, że ​​stwierdzenie jest prostsze, a kod bardziej czytelny:

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

Ale biblioteki Assertion / Matcher są koniecznością, ponieważ będą naprawdę dalej.
Załóżmy teraz, że Foo nie przechowuje instancji Strings ale Bars.
To bardzo powszechna potrzeba. Dzięki AssertJ twierdzenie jest nadal łatwe do napisania. Lepiej możesz stwierdzić, że zawartość listy jest równa, nawet jeśli klasa elementów nie nadpisuje, equals()/hashCode()podczas gdy sposób JUnit wymaga, aby:

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"));
}
davidxxx
źródło
7

Jeśli nie zależy Ci na kolejności elementów, polecam ListAssert.assertEqualsw junit-addons.

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

Dla leniwych użytkowników Maven:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>
Viktor Nordling
źródło
7
Uwaga: jeśli nie zależy Ci na kolejności elementów, powinieneś używać zestawu lub kolekcji, a nie listy.
Barett
11
Zgadzam się. Ta biblioteka jest obrzydliwa. Dlaczego, u licha, ListAssert.assertEquals () miałby domyślnie ustawiać bez kolejności?
Ryan,
5

Możesz użyć assertEquals w 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);
    }

Jeśli kolejność elementów jest inna, zwróci błąd.

Jeśli potwierdzasz listę obiektów modelu, powinieneś zastąpić metodę równości w określonym modelu.

    @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;
    }
Dhyan Mohandas
źródło
4

jeśli nie chcesz tworzyć listy tablic, możesz spróbować również tego

@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  
}
Arun Pratap Singh
źródło
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 ));
Ruslan Gafiullin
źródło
1

Nie odkrywaj na nowo koła!

Jest biblioteka Google Code, która robi to za Ciebie: Hamcrest

[Hamcrest] Udostępnia bibliotekę obiektów dopasowujących (znanych również jako ograniczenia lub predykaty), umożliwiając deklaratywne definiowanie reguł „dopasowywania”, które mogą być używane w innych frameworkach. Typowe scenariusze obejmują platformy testowe, biblioteki mockujące i reguły walidacji interfejsu użytkownika.

Czeski
źródło
0

Wiem, że istnieje już wiele opcji rozwiązania tego problemu, ale wolałbym wykonać następujące czynności, aby potwierdzić dwie listy w dowolnej odrze :

assertTrue(result.containsAll(expected) && expected.containsAll(result))
Kh.Taheri
źródło
czy to nie zawiedzie, jeśli kolejność nie zostanie dopasowana?
iec2011007
0

assertEquals(expected, result);pracuje dla mnie. Ponieważ ta funkcja pobiera dwa obiekty, możesz przekazać do niej wszystko.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}
ZNAK
źródło
-1

Nie rozumiem, wszystkie powyższe odpowiedzi dają dokładne rozwiązanie do porównania dwóch list Obiektów. Większość z powyższych podejść może być pomocna tylko w przestrzeganiu limitu porównań - Porównanie wielkości - Porównanie referencyjne

Ale jeśli mamy listy obiektów tej samej wielkości i różne dane na poziomie obiektów, to podejście porównawcze nie pomoże.

Myślę, że następujące podejście będzie doskonale działać z zastępowaniem równości i metody hashcode na obiekcie zdefiniowanym przez użytkownika.

Użyłem Xstream lib do zastępowania równości i hashcode, ale możemy zastąpić równa się i hashcode również za pomocą wygranej logiki / porównania.

Oto przykład w celach informacyjnych

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

Najważniejsze jest to, że możesz zignorować pola przez Adnotację (@XStreamOmitField), jeśli nie chcesz uwzględniać żadnych pól w równym sprawdzeniu obiektów. Istnieje wiele takich adnotacji do skonfigurowania, więc zapoznaj się dokładnie z adnotacjami w tej bibliotece.

Jestem pewien, że ta odpowiedź pozwoli Ci zaoszczędzić czas na zidentyfikowanie właściwego podejścia do porównania dwóch list obiektów :). Prosimy o komentarz, jeśli zauważysz jakiekolwiek problemy z tym związane.

Ganesa Vijayakumar
źródło