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 .


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

}

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

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


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

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

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

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

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.


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

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;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.