AssertEquals 2 Lists ignora el orden

82

Creo que debería ser una pregunta realmente simple. Pero de alguna manera no puedo encontrar la respuesta en Google.

Suponga que tengo 2 listas de cadenas. Primero contiene "Cadena A" y "Cadena B" , el segundo contiene "Cadena B" y "Cadena A" (observe la diferencia en el orden). Quiero probarlos con JUnit para verificar si contienen exactamente las mismas cadenas.

¿Hay alguna afirmación que compruebe la igualdad de cadenas que ignoran el orden? Por ejemplo, org.junit.Assert.assertEquals arroja AssertionError

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

La solución consiste en ordenar las listas en primer lugar y luego pasarlas a la afirmación. Pero quiero que mi código sea lo más simple y limpio posible.

Yo uso Hamcrest 1.3 , JUnit 4.11 , Mockito 1.9.5 .

kukis
fuente
3
list1.removeAll(list2)debe dejar list1vacío. Supongo que puedes aprovechar esto para conseguir lo que quieres.
SudoRahul
6
containsAlly removeAllson O(n²)para listas mientras las ordena y la prueba de igualdad es O(nlogn). Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2));también está limpio.
Alexis C.
1
posible duplicado de Hamcrest comparar colecciones
Joe
@SudoRahul: ¿Qué sucede si no desea modificar una lista eliminando todo?
Erran Morad
@BoratSagdiyev - Dado que eso no era una restricción del OP, lo sugerí. Pero si eso es una restricción, entonces la respuesta aceptada para esta pregunta resuelve el problema en cuestión.
SudoRahul

Respuestas:

92

Como mencionas que usas Hamcrest, elegiría uno de los Matchers de la colección

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

}
cheffe
fuente
5
Consulte también mi respuesta stackoverflow.com/a/38262680/297710 que muestra cómo mejorar los emparejadores de Hamcrest y evitar ".toArray ()" en cada
aserción
57

Puede usar List.containsAll con JUnit's assertTrue para verificar que la primera lista contiene todos los elementos del segundo, y viceversa.

assertTrue(first.size() == second.size() && 
    first.containsAll(second) && second.containsAll(first));
robertoia
fuente
2
@kukis Depende, ¿quieres buscar duplicados?
robertoia
4
Sí, por supuesto. 2 Listas dadas deben ser exactamente iguales ignorando el orden.
kukis
2
@kukis Revisa el comentario de ZouZou sobre tu pregunta.
robertoia
1
... podría incluir assertEquals(first.size(), second.size())... entonces debería funcionar como se esperaba
definitivamente indefinible
17
Esto no funciona con duplicados en la lista. Aquí hay un ejemplo para demostrar: 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)); En este ejemplo, ambas afirmaciones no detectan que las listas son realmente diferentes. @AlexWorden menciona CollectionUtils.isEqualCollection () de Apache Commons Collections que, para este ejemplo, detecta correctamente que las colecciones no son iguales.
desilvai
11

Aquí hay una solución que evita la complejidad cuadrática (iterar sobre las listas varias veces). Esto utiliza la clase CollectionUtils de Apache Commons para crear un mapa de cada elemento a un recuento de frecuencia en la lista. Luego, simplemente compara los dos mapas.

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

También acabo de ver CollectionUtils.isEqualCollection que afirma hacer exactamente lo que se solicita aquí ...

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

Alex Worden
fuente
4

Con AssertJ, containsExactlyInAnyOrder()o containsExactlyInAnyOrderElementsOf()es lo que necesitas:

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");
    }    
}
davidxxx
fuente
3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);
Tinyfool
fuente
2

Llego tarde a la fiesta, pero esta es mi solución usando solo Junit. Cualquier pensamiento es bienvenido.

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);
}
Sujit Joshi
fuente
1

Tenga en cuenta que la solución de Roberto Izquierdo tiene una complejidad cuadrática en general. La solución en HashSets siempre tiene una complejidad lineal:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));
leventov
fuente
2
Ese enfoque no funcionará. Si el primero es ("Cadena A") y el segundo es ("Cadena A", "Cadena A"), no son las mismas listas.
Alexis C.
4
No puedes comprobar el tamaño. Si el primero es ("s1", "s2", "s3" ,"s1")y el segundo ("s2", "s1", "s3" ,"s2");no son la misma lista.
Alexis C.
@ZouZou, la solución aceptada tiene el mismo problema. Sugirió la única solución realmente correcta. Si respondes, la votaré.
leventov
@ZouZou No son la misma lista, pero contienen exactamente las mismas cadenas. OP, aclara ?. Además, conviértalo en una respuesta y también votaré :) No pensé en eso.
robertoia
2
Esto todavía no es correcto para todos los casos ("A", "A", "B") se comparará como igual a ("A", "B", "B")
Tim B
1

Para una solución rápida, comprobaría en ambos sentidos:

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

Y al intentar con una situación en la que el número de los mismos elementos es diferente (por ejemplo, 1, 1, 2 y 1, 2, 2) no obtuve falsos positivos.

Kristjan Veskimäe
fuente
1
Tu código aún falla. Vea este ejemplo - @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 (lista2.containsTodo (lista1)); }
Erran Morad
1

Puede usar ListAssert que viene en el jar junit-addons.

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));
Akungta
fuente
0

Parece que las otras respuestas hacen referencia a utilidades de terceros, son incorrectas o ineficaces.

Aquí hay una solución básica O (N) en 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;
}
Daniel Avery
fuente