Afirmar es igual a 2 listas en Junit

164

¿Cómo puedo hacer una afirmación de igualdad entre listas en un caso de prueba JUnit ? La igualdad debe estar entre el contenido de la lista.

Por ejemplo:

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
fuente
55
Me gusta usar assertArrayEqualshoy en día. Usar en combinación con List#toArray.
Thibstars
@Thibstars - Votaría eso como respuesta.
dfrankow
2
assertArrayEqualsrequiere que obtengas matrices de tus listas. Puede operar directamente en la lista usandoassertIterableEquals
ThetaSinner el
assertIterableEqualsdisponible para jUnit5 @ThetaSinner
iec2011007

Respuestas:

168

Me doy cuenta de que esto se hizo hace un par de años, probablemente esta característica no estaba disponible entonces. Pero ahora, es fácil hacer esto:

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

Si tiene una versión reciente de Junit instalada con Hamcrest, simplemente agregue estas importaciones:

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
fuente
3
System.out.println(actual == expected);devolverá falso, pero System.out.println(actual.equals(expected));devolverá verdadero.
Catfish
@Catfish, sí, eso es confuso, ¿no? Creo que estaba demostrando que el matcher está usando en .equals(..)lugar de ==?
djeikyb
2
¿Cómo es eso mejor que afirmar Equals?
Raedwald
1
@Raedwald la salida cuando la afirmación falla. Intentaré volver más tarde para editar la diferencia. Hamcrest Matchers puede generar mensajes de falla detallados. Es posible que Junit simplemente sobrecargue afirmar Igual con bondad similar. pero principalmente junit proporciona características de prueba de unidad central, y hamcrest proporciona una biblioteca de descriptores de diferencia de objetos agradable de tener.
djeikyb
29

No se transforme en una cadena y compare. Esto no es bueno para el rendimiento. En el junit, dentro de Corematchers, hay una coincidencia para esto =>hasItems

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

Esta es la mejor manera que conozco para verificar elementos en una lista.

Andre Fonseca
fuente
2
Debe ser la respuesta elegida, con una nota: también debe verificar que no haya más elementos en la lista además de lo que desea. Tal vez use:Assert.assertEquals(4,yourList.size());
yoni
o con una sola línea:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602
3
"Esto no es bueno para el rendimiento". → No estoy seguro de hasta qué punto se debe tener en cuenta el rendimiento al escribir pruebas unitarias ... Pero claro, comparar cadenas a través de su toString()versión no es la mejor manera.
walen
21

Esta es una respuesta heredada, adecuada para JUnit 4.3 y versiones posteriores. La versión moderna de JUnit incluye mensajes de falla legibles integrados en el método afirmar ese. Prefiere otras respuestas a esta pregunta, si es posible.

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

Para el registro, como @Paul mencionó en su comentario a esta respuesta, dos Lists son iguales:

si y solo si el objeto especificado es también una lista, ambas listas tienen el mismo tamaño y todos los pares de elementos correspondientes en las dos listas son iguales. (Dos elementos e1y e2son iguales si (e1==null ? e2==null : e1.equals(e2))). En otras palabras, dos listas se definen como iguales si contienen los mismos elementos en el mismo orden. Esta definición garantiza que el método equals funcione correctamente en diferentes implementaciones de la Listinterfaz.

Vea los JavaDocs de la Listinterfaz .

Bart Kiers
fuente
1
¿Quiere decir que se espera que.equals (a) se encargará de afirmar los objetos que contiene la lista?
Kamal
1
From List javadoc: compara el objeto especificado con esta lista para la igualdad. Devuelve verdadero si y solo si el objeto especificado es también una lista, ambas listas tienen el mismo tamaño y todos los pares de elementos correspondientes en las dos listas son iguales. (Dos elementos e1 y e2 son iguales si (e1 == null? E2 == null: e1.equals (e2)).) En otras palabras, dos listas se definen como iguales si contienen los mismos elementos en el mismo orden . Esta definición garantiza que el método equals funcione correctamente en diferentes implementaciones de la interfaz List.
Paul McKenzie
1
Esto, por desgracia, proporciona un mensaje de error menos que útil. He encontrado que es mejor escribir una clase de utilidad que realice un bucle para que pueda ver qué elementos son diferentes.
Michael Lloyd Lee mlk
@mlk, tal vez, pero dudo en escribir un método de utilidad personalizado para tal cosa. ¿Qué pasa con el mensaje de error que edité ahora?
Bart Kiers
@mlk Estoy de acuerdo en que podría ser mejor escribir un bucle para probar cada elemento para que sepa exactamente qué es diferente. Depende de qué tipos de objetos hay en la lista. Si son cadenas, simplemente diga "a =" + a debería estar bien, pero si son objetos complejos (otras listas o algo que no tiene una buena implementación de cadena), podría ser más fácil probarlos individualmente
Matt N
20

assertEquals(Object, Object)de JUnit4 / JUnit 5 o assertThat(actual, is(expected));de Hamcrest propuesto en las otras respuestas funcionarán solo como ambos equals()y toString()se anulan para las clases (y profundamente) de los objetos comparados.

Es importante porque la prueba de igualdad en la afirmación se basa equals()y el mensaje de falla de prueba se basa en toString()los objetos comparados.
Para clases integradas como String, Integery así para ... no hay problema, ya que anulan ambas equals()y toString(). Por lo tanto, es perfectamente válido afirmar List<String>o List<Integer>con assertEquals(Object,Object).
Y sobre este asunto: debe anular equals()en una clase porque tiene sentido en términos de igualdad de objetos, no solo para facilitar las afirmaciones en una prueba con JUnit.
Para hacer las afirmaciones más fáciles, tienes otras formas.
Como buena práctica, estoy a favor de las bibliotecas de aserción / comparación.

Aquí hay una solución AssertJ .

org.assertj.core.api.ListAssert.containsExactly() es lo que necesita: verifica que el grupo real contenga exactamente los valores dados y nada más, en el orden indicado en el javadoc.

Suponga una Fooclase donde agrega elementos y donde puede obtenerlos.
Una prueba unitaria de Fooeso afirma que las dos listas tienen el mismo contenido podría verse así:

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

Un buen punto de AssertJ es que declarar un Listcomo se espera no es necesario: hace que la afirmación sea más recta y el código sea más legible:

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

Pero las bibliotecas Assertion / matcher son imprescindibles porque estas realmente irán más allá.
Supongamos ahora que Foo no almacena Strings sino Bars instancias.
Esa es una necesidad muy común. Con AssertJ, la afirmación sigue siendo simple de escribir. Mejor puede afirmar que el contenido de la lista es igual incluso si la clase de los elementos no se anula, equals()/hashCode()mientras que JUnit requiere que:

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

Si no le importa el orden de los elementos, lo recomiendo ListAssert.assertEqualsen junit-addons.

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

Para usuarios perezosos de Maven:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>
Viktor Nordling
fuente
77
Nota: Si no le importa el orden de los elementos, debería usar un Conjunto o Colección, no una Lista.
Barett
11
Estoy de acuerdo. Esta biblioteca es asquerosa. ¿Por qué demonios ListAssert.assertEquals () predeterminado a sin orden?
Ryan
5

Puede usar afirmarEquals en 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);
    }

Si el orden de los elementos es diferente, devolverá un error.

Si está afirmando una lista de objetos de modelo, debe anular el método de igualdad en el modelo específico.

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

si no desea crear una lista de matriz, puede probar esto también

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

¡No reinventes la rueda!

Hay una biblioteca de códigos de Google que hace esto por usted: Hamcrest

[Hamcrest] Proporciona una biblioteca de objetos de comparación (también conocidos como restricciones o predicados) que permite que las reglas de "coincidencia" se definan de manera declarativa, para ser utilizadas en otros marcos. Los escenarios típicos incluyen marcos de prueba, bibliotecas simuladas y reglas de validación de la interfaz de usuario.

Bohemio
fuente
0

Sé que ya hay muchas opciones para resolver este problema, pero preferiría hacer lo siguiente para afirmar dos listas en cualquier orden :

assertTrue(result.containsAll(expected) && expected.containsAll(result))
Kh.Taheri
fuente
¿Esto no falla si el pedido no coincide?
iec2011007
0

assertEquals(expected, result);funciona para mi. Como esta función obtiene dos objetos, puede pasarle cualquier cosa.

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

No sé esto, todas las respuestas anteriores están dando la solución exacta para comparar dos listas de objetos. La mayoría de los enfoques anteriores pueden ser útiles solo para el siguiente límite de comparaciones - Comparación de tamaño - Comparación de referencia

Pero si tenemos listas de objetos del mismo tamaño y datos diferentes en el nivel de los objetos, estos enfoques de comparación no ayudarán.

Creo que el siguiente enfoque funcionará perfectamente con la anulación de los métodos igual y hashcode en el objeto definido por el usuario.

Utilicé Xstream lib para anular equals y hashcode, pero también podemos anular equals y hashcode sin la lógica / comparación ganada.

Aquí está el ejemplo para su referencia.

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

Lo más importante es que puede ignorar los campos por Anotación (@XStreamOmitField) si no desea incluir ningún campo en la verificación igual de Objetos. Hay muchas anotaciones como esta para configurar, así que eche un vistazo a las anotaciones de esta lib.

Estoy seguro de que esta respuesta le ahorrará tiempo para identificar el enfoque correcto para comparar dos listas de objetos :). Comenta si ves algún problema al respecto.

Ganesa Vijayakumar
fuente