Assert entspricht 2 Listen in Junit

164

Wie kann ich in einem JUnit- Testfall eine Gleichheitserklärung zwischen Listen abgeben ? Gleichheit sollte zwischen dem Inhalt der Liste bestehen.

Beispielsweise:

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
quelle
5
Ich benutze assertArrayEqualsheutzutage gerne . Verwendung in Kombination mit List#toArray.
Thibstars
@Thibstars - Ich würde das als Antwort positiv bewerten.
dfrankow
2
assertArrayEqualserfordert, dass Sie Arrays von Ihren Listen erhalten. Sie können direkt auf der Liste mitassertIterableEquals
ThetaSinner
assertIterableEqualsverfügbar für jUnit5 @ThetaSinner
iec2011007

Antworten:

168

Mir ist klar, dass dies vor ein paar Jahren gefragt wurde, wahrscheinlich gab es diese Funktion damals noch nicht. Aber jetzt ist es einfach:

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

Wenn Sie eine aktuelle Version von Junit mit hamcrest installiert haben, fügen Sie einfach die folgenden Importe hinzu:

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
quelle
3
System.out.println(actual == expected);wird false zurückgeben, System.out.println(actual.equals(expected));wird aber true zurückgeben.
Wels
@ Catfish ja, das ist verwirrend, nicht wahr? Ich glaube, ich habe demonstriert, dass der Matcher .equals(..)anstelle von ==?
Djeikyb
2
Wie ist das besser als assertEquals?
Raedwald
1
@Raedwald die Ausgabe, wenn die Zusicherung fehlschlägt. Ich werde versuchen, später wiederzukommen, um den Unterschied zu bearbeiten. Hamcrest-Matcher können detaillierte Fehlermeldungen generieren. Es ist für Junit möglich, assertEquals einfach mit ähnlicher Güte zu überladen. Aber meistens bietet junit Kernfunktionen für Unit-Tests, und hamcrest bietet eine ansprechende Bibliothek zur Beschreibung von Objektunterschieden.
Djeikyb
29

Nicht in String verwandeln und vergleichen. Das ist nicht gut für die Leistung. In der Junit, innerhalb von Corematchers, gibt es einen Matcher dafür =>hasItems

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

Dies ist der bessere Weg, um Elemente in einer Liste zu überprüfen.

Andre Fonseca
quelle
2
Sollte die gewählte Antwort sein, mit einem Hinweis: Sie müssen auch überprüfen, ob die Liste außer den gewünschten Elementen keine weiteren Elemente enthält. Vielleicht verwenden:Assert.assertEquals(4,yourList.size());
Yoni
oder mit einer einzelnen Zeile:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602
3
"Das ist nicht gut für die Leistung." → Ich bin mir nicht sicher, inwieweit man die Leistung beim Schreiben von Komponententests berücksichtigen sollte ... Aber sicher ist der Vergleich von Zeichenfolgen über ihre toString()Version nicht der beste Weg.
Wal
21

Dies ist eine Legacy-Antwort, die für JUnit 4.3 und niedriger geeignet ist. Die moderne Version von JUnit enthält integrierte lesbare Fehlermeldungen in der assertThat-Methode. Bevorzugen Sie nach Möglichkeit andere Antworten auf diese Frage.

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

Für die Aufzeichnung, wie @Paul in seinem Kommentar zu dieser Antwort erwähnte, zwei List s gleich:

genau dann, wenn das angegebene Objekt auch eine Liste ist, haben beide Listen die gleiche Größe und alle entsprechenden Elementpaare in den beiden Listen sind gleich. (Zwei Elemente e1und e2sind gleich wenn(e1==null ? e2==null : e1.equals(e2)) .) Mit anderen Worten, zwei Listen werden als gleich definiert, wenn sie dieselben Elemente in derselben Reihenfolge enthalten. Diese Definition stellt sicher, dass die Methode equals in verschiedenen Implementierungen der ListSchnittstelle ordnungsgemäß funktioniert .

Siehe die JavaDocs der ListSchnittstelle .

Bart Kiers
quelle
1
Sie meinen also erwartet. Gleichungen (a) kümmern sich um die Behauptung der Objekte, die die Liste enthält?
Kamal
1
Aus Liste javadoc: Vergleicht das angegebene Objekt mit dieser Liste auf Gleichheit. Gibt nur dann true zurück, wenn das angegebene Objekt auch eine Liste ist, beide Listen dieselbe Größe haben und alle entsprechenden Elementpaare in den beiden Listen gleich sind. (Zwei Elemente e1 und e2 sind gleich, wenn (e1 == null? E2 == null: e1.equals (e2)).) Mit anderen Worten, zwei Listen werden als gleich definiert, wenn sie dieselben Elemente in derselben Reihenfolge enthalten . Diese Definition stellt sicher, dass die Methode equals in verschiedenen Implementierungen der List-Schnittstelle ordnungsgemäß funktioniert.
Paul McKenzie
1
Dies liefert leider weniger als hilfreiche Fehlermeldungen. Ich habe es besser gefunden, eine Utility-Klasse zu schreiben, die eine Schleife ausführt, damit Sie sehen können, welche Elemente unterschiedlich sind.
Michael Lloyd Lee mlk
@mlk vielleicht, aber ich zögere, eine benutzerdefinierte Dienstprogrammmethode für so etwas zu schreiben. Was ist mit der Fehlermeldung, die ich gerade bearbeitet habe?
Bart Kiers
@mlk Ich stimme zu, dass es möglicherweise besser ist, eine Schleife zu schreiben, um jedes Element zu testen, damit Sie genau wissen, was anders ist. Dies hängt davon ab, welche Objekttypen in der Liste enthalten sind. Wenn sie Strings sind, dann sagen Sie einfach „a =“ + eine sollte in Ordnung sein, aber wenn sie komplexe Objekte (andere Listen, oder etwas , das nicht eine gute toString Implementierung hat) sind, könnte es einfacher sein , sie einzeln zu testen
Matt N
20

assertEquals(Object, Object)von JUnit4 / JUnit 5 oder assertThat(actual, is(expected));von Hamcrest, die in den anderen Antworten vorgeschlagen wurden, funktionieren nur als beide equals()undtoString() werden für die Klassen (und tief) der verglichenen Objekte überschrieben.

Es ist wichtig, weil der Gleichheitstest in der Behauptung davon abhängt equals() auf toString()die verglichenen Objekte und die Testfehlermeldung auf diese stützt .
Für integrierte Klassen wie String, Integerund so für ... kein Problem , da diese überschreiben beide equals()und toString(). Es ist also durchaus gültig zu behaupten List<String>oder List<Integer>mit assertEquals(Object,Object).
Und zu diesem Thema: Sie müssen equals()in einer Klasse überschreiben, weil dies im Hinblick auf die Objektgleichheit sinnvoll ist, nicht nur, um Aussagen in einem Test mit JUnit zu vereinfachen.
Um die Behauptungen zu vereinfachen, haben Sie andere Möglichkeiten.
Als gute Praxis bevorzuge ich Assertion / Matcher-Bibliotheken.

Hier ist eine AssertJ- Lösung.

org.assertj.core.api.ListAssert.containsExactly() ist das, was Sie brauchen: Es überprüft, ob die tatsächliche Gruppe genau die angegebenen Werte und nichts anderes enthält, in der Reihenfolge, wie im Javadoc angegeben.

Angenommen, eine FooKlasse, in der Sie Elemente hinzufügen und in der Sie diese erhalten können.
Ein Unit-Test davon bestätigt Foo, dass die beiden Listen den gleichen Inhalt haben könnten:

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

Ein guter Punkt von AssertJ ist, dass es Listunnötig ist, a wie erwartet zu deklarieren : Dadurch wird die Assertion gerader und der Code lesbarer:

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

Assertion / Matcher-Bibliotheken sind jedoch ein Muss, da diese wirklich weiter gehen werden.
Nehmen wir jetzt an, dass Foo nicht Strings, sondern Bars Instanzen gespeichert werden.
Das ist ein sehr häufiges Bedürfnis. Mit AssertJ ist die Behauptung immer noch einfach zu schreiben. Besser können Sie behaupten, dass der Listeninhalt gleich ist, auch wenn die Klasse der Elemente nicht überschreibt, equals()/hashCode()während JUnit dies erfordert:

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
quelle
7

Wenn Sie sich nicht für die Reihenfolge der Elemente interessieren, empfehle ich ListAssert.assertEquals in Junit-Addons.

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

Für faule Maven-Benutzer:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>
Viktor Nordling
quelle
7
Hinweis: Wenn Sie sich nicht um die Reihenfolge der Elemente kümmern, sollten Sie einen Satz oder eine Sammlung verwenden, keine Liste.
Barett
11
Genau. Diese Bibliothek ist brutto. Warum um alles in der Welt sollte ListAssert.assertEquals () standardmäßig orderless sein?
Ryan
5

Sie können assertEquals in junit verwenden .

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

Wenn die Reihenfolge der Elemente unterschiedlich ist, wird ein Fehler zurückgegeben.

Wenn Sie eine Modellobjektliste aktivieren, sollten Sie die Methode equals im jeweiligen Modell überschreiben.

    @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
quelle
4

Wenn Sie keine Array-Liste erstellen möchten, können Sie dies auch versuchen

@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
quelle
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
quelle
1

Das Rad nicht neu erfinden!

Es gibt eine Google Code-Bibliothek, die dies für Sie erledigt : Hamcrest

[Hamcrest] Bietet eine Bibliothek von Matcher-Objekten (auch als Einschränkungen oder Prädikate bezeichnet), mit denen 'Match'-Regeln deklarativ definiert und in anderen Frameworks verwendet werden können. Typische Szenarien sind das Testen von Frameworks, das Verspotten von Bibliotheken und Validierungsregeln für die Benutzeroberfläche.

Böhmisch
quelle
0

Ich weiß, dass es bereits viele Möglichkeiten gibt, dieses Problem zu lösen, aber ich würde lieber Folgendes tun, um zwei Listen in irgendeiner Reihenfolge zu behaupten :

assertTrue(result.containsAll(expected) && expected.containsAll(result))
Kh.Taheri
quelle
Wird dies nicht fehlschlagen, wenn die Bestellung nicht übereinstimmt?
iec2011007
0

assertEquals(expected, result);funktioniert bei mir. Da diese Funktion zwei Objekte erhält, können Sie alles an sie übergeben.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}
KENNZEICHEN
quelle
-1

Ich weiß nicht, dass alle oben genannten Antworten die genaue Lösung für den Vergleich zweier Objektlisten darstellen. Die meisten der oben genannten Ansätze können hilfreich sein, um nur die Vergleichsgrenze einzuhalten - Größenvergleich - Referenzvergleich

Wenn wir jedoch gleich große Objektlisten und unterschiedliche Daten auf Objektebene haben, helfen diese Vergleichsansätze nicht weiter.

Ich denke, der folgende Ansatz funktioniert perfekt mit überschreibenden Gleichheits- und Hashcode-Methoden für das benutzerdefinierte Objekt.

Ich habe Xstream lib zum Überschreiben von Equals und Hashcode verwendet, aber wir können Equals und Hashcode auch durch gewonnene Logik / Vergleich überschreiben.

Hier ist das Beispiel als Referenz

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

Das Wichtigste ist, dass Sie die Felder durch Annotation (@XStreamOmitField) ignorieren können, wenn Sie keine Felder in die gleiche Prüfung von Objekten aufnehmen möchten. Es gibt viele solche Annotationen, die konfiguriert werden müssen. Schauen Sie sich also die Annotationen dieser Bibliothek genau an.

Ich bin sicher, diese Antwort wird Ihnen Zeit sparen, um den richtigen Ansatz für den Vergleich zweier Objektlisten zu finden :). Bitte kommentieren Sie, wenn Sie Probleme dazu sehen.

Ganesa Vijayakumar
quelle