AssertEquals 2 Listen ignorieren die Reihenfolge


80

Das sollte eine wirklich einfache Frage sein, glaube ich. Aber irgendwie kann ich in Google keine Antwort finden.

Angenommen, ich habe 2 Listen mit Zeichenfolgen. Der erste enthält "String A" und "String B" , der zweite "String B" und "String A" (Unterschied in der Reihenfolge beachten). Ich möchte sie mit JUnit testen, um zu überprüfen, ob sie genau die gleichen Strings enthalten.

Gibt es eine Behauptung, die die Gleichheit von Strings überprüft, die die Reihenfolge ignorieren? Für das angegebene Beispiel löst org.junit.Assert.assertEquals AssertionError aus

java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>

Um dies zu umgehen, müssen zuerst die Listen sortiert und dann an die Zusicherung übergeben werden. Aber ich möchte, dass mein Code so einfach und sauber wie möglich ist.

Ich benutze Hamcrest 1.3 , JUnit 4.11 , Mockito 1.9.5 .


3
list1.removeAll(list2)sollte list1leer lassen. Ich denke, Sie können darauf aufbauen, um das zu bekommen, was Sie wollen.
SudoRahul

6
containsAllund removeAllsind O(n²)für Listen beim Sortieren und Testen auf Gleichheit ist O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));ist auch sauber.
Alexis C.

1
mögliches Duplikat von Hamcrest vergleichen Sammlungen
Joe

@SudoRahul - Was ist, wenn Sie eine Liste nicht ändern möchten, indem Sie alle entfernen?
Erran Morad

@ BoratSagdiyev - Da dies keine Einschränkung des OP war, schlug ich dies vor. Wenn dies jedoch eine Einschränkung ist, löst die akzeptierte Antwort auf diese Frage das vorliegende Problem.
SudoRahul

Antworten:


89

Wenn Sie erwähnen, dass Sie Hamcrest verwenden, würde ich einen der Matcher der Sammlung auswählen

import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertThat;

public class CompareListTest {

    @Test
    public void compareList() {
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");

        assertThat("List equality without order", 
            actual, containsInAnyOrder(expected.toArray()));
    }

}

5
Siehe auch meine Antwort stackoverflow.com/a/38262680/297710, die zeigt, wie man Hamcrest-Matcher verbessert und ".toArray ()" in jeder Behauptung mit
enthältInAnyOrder

56

Sie können List.containsAll mit assertTrue von JUnit verwenden, um zu überprüfen, ob die erste Liste alle Elemente der zweiten enthält und umgekehrt.

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));

2
@kukis Es kommt darauf an, ob Sie nach Duplikaten suchen möchten.
Robertoia

4
Ja natürlich. 2 gegebene Listen müssen genau gleich sein, wobei die Reihenfolge ignoriert wird.
Kukis

2
@kukis Überprüfen Sie dann ZouZous Kommentar zu Ihrer Frage.
Robertoia

1
..kann einschließen assertEquals(first.size(), second.size())..dann sollte es wie erwartet funktionieren
definitiv undefinierbar

16
Dies funktioniert nicht mit Duplikaten in der Liste. Hier ein Beispiel zur Veranschaulichung: List<String> list1 = Arrays.asList("a", "a", "b"); List<String> list2 = Arrays.asList("a", "b", "b"); assertEquals(list1.size(), list2.size()); assertTrue(list1.containsAll(list2) && list2.containsAll(list1)); In diesem Beispiel erkennen beide Zusicherungen nicht, dass die Listen tatsächlich unterschiedlich sind. @AlexWorden erwähnt die CollectionUtils.isEqualCollection () von Apache Commons Collections, die in diesem Beispiel korrekt erkennt, dass die Sammlungen nicht gleich sind.
Desilvai

11

Hier ist eine Lösung, die quadratische Komplexität vermeidet (mehrmaliges Durchlaufen der Listen). Hierbei wird die Apache Commons CollectionUtils-Klasse verwendet, um eine Zuordnung jedes Elements zu einer Häufigkeit selbst in der Liste zu erstellen. Anschließend werden einfach die beiden Karten verglichen.

Assert.assertEquals("Verify same metrics series",
    CollectionUtils.getCardinalityMap(expectedSeriesList),
    CollectionUtils.getCardinalityMap(actualSeriesList));

Ich habe auch gerade CollectionUtils.isEqualCollection entdeckt, die behauptet, genau das zu tun, was hier angefordert wird ...

https://commons.apache.org/proper/commons-collections/apidocs/index.html?org/apache/commons/collections4/CollectionUtils.html


4

Mit AssertJ containsExactlyInAnyOrder()oder containsExactlyInAnyOrderElementsOf()ist das, was Sie brauchen:

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

import java.util.Arrays;
import java.util.List;

public class CompareListTest {

    @Test
    public void compareListWithTwoVariables() {
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrderElementsOf(expected);
    }

    @Test
    public void compareListWithInlineExpectedValues() {
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrder("String A", "String B");
    }    
}

3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

2

Ich bin zu spät zur Party, aber hier ist meine Lösung nur mit Junit. Alle Gedanken sind willkommen.

List<String> actual = new ArrayList<>();
actual.add("A");
actual.add("A");
actual.add("B");

List<String> expected = new ArrayList<>();
actual.add("A");
actual.add("B");
actual.add("B");

//Step 1: assert for size
assertEquals(actual.size(), expected.size());

//Step 2: Iterate
for(String e: expected){
    assertTrue(actual.contains(e));
    actual.remove(e);
}

1

Beachten Sie, dass die Lösung von Roberto Izquierdo im Allgemeinen eine quadratische Komplexität aufweist. Die Lösung auf HashSets ist immer linear komplex:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));

2
Dieser Ansatz wird nicht funktionieren. Wenn der erste ("String A") und der zweite ("String A", "String A") ist, handelt es sich nicht um dieselben Listen.
Alexis C.

4
Sie können die Größe nicht überprüfen. Wenn erstens ("s1", "s2", "s3" ,"s1")und zweitens ist ("s2", "s1", "s3" ,"s2");, sind sie nicht dieselbe Liste.
Alexis C.

@ZouZou die akzeptierte Lösung hat das gleiche Problem. Sie haben die einzig wirklich richtige Lösung vorgeschlagen. Wenn Sie eine Antwort geben, werde ich sie positiv bewerten.
Leventov

@ZouZou Sie sind nicht die gleiche Liste, aber sie enthalten genau die gleichen Strings. OP, klären?. Machen Sie es auch eine Antwort und ich werde auch upvoten :) habe nicht daran gedacht.
Robertoia

2
Dies ist immer noch nicht in allen Fällen korrekt ("A", "A", "B") wird mit ("A", "B", "B")
Tim B

1

Für eine schnelle Lösung würde ich beide Möglichkeiten prüfen:

assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));

Und wenn ich es mit einer Situation versuche, in der die Anzahl der gleichen Elemente unterschiedlich ist (z. B. 1, 1, 2 und 1, 2, 2), habe ich keine falsch positiven Ergebnisse erhalten.


1
Ihr Code schlägt immer noch fehl. Siehe dieses Beispiel - @Test public void test1 () {List <String> list1 = Arrays.asList ("a", "a", "b"); List <String> list2 = Arrays.asList ("a", "b", "b"); Assert.assertTrue (list1.containsAll (list2)); Assert.assertTrue (list2.containsAll (list1)); }
Erran Morad


0

Die anderen Antworten beziehen sich entweder auf Dienstprogramme von Drittanbietern, sind falsch oder ineffizient.

Hier ist eine O (N) Vanille-Lösung in Java 8.

public static void assertContainsSame(Collection<?> expected, Collection<?> actual)
{
    assert expected.size() == actual.size();

    Map<Object, Long> counts = expected.stream()
        .collect(Collectors.groupingBy(
                item -> item,
                Collectors.counting()));

    for (Object item : actual)
        assert counts.merge(item, -1L, Long::sum) != -1L;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.