Assert è uguale a 2 liste in Junit


165

Come posso fare un'asserzione di uguaglianza tra gli elenchi in un caso di test JUnit ? L'uguaglianza dovrebbe essere tra il contenuto dell'elenco.

Per esempio:

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

5
Mi piace usare al assertArrayEqualsgiorno d'oggi. Utilizzare in combinazione con List#toArray.
Thibstars,

@Thibstars - L'ho votato come risposta.
domenica

2
assertArrayEqualsrichiede di ottenere array dalle proprie liste. Puoi operare direttamente nell'elenco usandoassertIterableEquals
ThetaSinner il

assertIterableEqualsdisponibile per jUnit5 @ThetaSinner
iec2011007

Risposte:


170

Mi rendo conto che questo è stato chiesto un paio di anni fa, probabilmente questa funzionalità non era disponibile in quel momento. Ma ora è facile fare solo questo:

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

Se hai installato una versione recente di Junit con hamcrest, aggiungi solo queste importazioni:

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


3
System.out.println(actual == expected);restituirà false, ma System.out.println(actual.equals(expected));restituirà true.
Pesce gatto

@Catfish sì, è confuso non è vero. Penso che stavo dimostrando che il matcher sta usando .equals(..)invece di ==?
djeikyb,

2
In che modo è meglio di assertEquals?
Raedwald

1
@Raedwald l'output quando l'asserzione fallisce. proverò a tornare più tardi per modificare la differenza. gli abbinamenti hamcrest possono generare messaggi dettagliati di errore. è possibile che junit sovraccarichi semplicemente assertEquals con bontà simile. ma per lo più junit fornisce funzionalità di test dell'unità di base e hamcrest fornisce una libreria descrittore di differenze di oggetti piacevole da avere.
djeikyb,

29

Non trasformare in stringa e confrontare. Questo non è buono per le prestazioni. Nella junit, all'interno di Corematchers, c'è un matcher per questo =>hasItems

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

Questo è il modo migliore che conosco per controllare gli elementi in un elenco.


2
Dovrebbe essere la risposta scelta, con una nota: devi anche verificare che non ci siano più voci nell'elenco oltre a quello che desideri. Forse usare:Assert.assertEquals(4,yourList.size());
yoni

o con una sola riga:assertThat(yourList.toArray(), arrayContainingInAnyOrder(1,2,3,4,5));
user1778602

3
"Questo non è buono per le prestazioni." → Non sono sicuro di quale grado si debba tener conto delle prestazioni quando si scrivono unit test ... Ma certo, confrontare le stringhe tramite la loro toString()versione non è il modo migliore.
walen

21

Questa è una risposta legacy, adatta a JUnit 4.3 e precedenti. La versione moderna di JUnit include messaggi di errore leggibili integrati nel metodo assertThat. Preferisci altre risposte a questa domanda, se possibile.

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

Per la cronaca, come menzionato da @Paul nel suo commento a questa risposta, due Lists sono uguali:

se e solo se l'oggetto specificato è anche un elenco, entrambi gli elenchi hanno le stesse dimensioni e tutte le coppie corrispondenti di elementi nei due elenchi sono uguali. (Due elementi e1e e2sono uguali se (e1==null ? e2==null : e1.equals(e2)).) In altre parole, due elenchi sono definiti uguali se contengono gli stessi elementi nello stesso ordine. Questa definizione garantisce che il metodo equals funzioni correttamente in diverse implementazioni Listdell'interfaccia.

Vedi i JavaDocs Listdell'interfaccia .


1
Quindi intendi previsto.equals (a) si occuperà di affermare gli oggetti che la lista contiene?
Kamal,

1
Dall'elenco javadoc: confronta l'oggetto specificato con questo elenco per l'uguaglianza. Restituisce vero se e solo se l'oggetto specificato è anche un elenco, entrambi gli elenchi hanno le stesse dimensioni e tutte le coppie di elementi corrispondenti nei due elenchi sono uguali. (Due elementi e1 ed e2 sono uguali se (e1 == null? E2 == null: e1.equals (e2)). In altre parole, due elenchi sono definiti uguali se contengono gli stessi elementi nello stesso ordine . Questa definizione garantisce che il metodo equals funzioni correttamente in diverse implementazioni dell'interfaccia Elenco.
Paul McKenzie,

1
Questo ahimè fornisce un messaggio di errore tutt'altro che utile. Ho trovato meglio scrivere una classe di utilità che esegue un ciclo in modo da poter vedere quali elementi sono diversi.
Michael Lloyd Lee mlk,

@mlk, forse, ma sono titubante a scrivere un metodo di utilità personalizzato per una cosa del genere. Che dire del messaggio di errore che ho modificato proprio ora?
Bart Kiers,

@mlk Sono d'accordo che potrebbe essere meglio scrivere un ciclo per testare ogni elemento in modo da sapere esattamente cosa è diverso. Dipende dai tipi di oggetti presenti nell'elenco. Se sono stringhe, basta dire "a =" + a dovrebbe andare bene, ma se sono oggetti complessi (altri elenchi o qualcosa che non ha una buona implementazione di String), potrebbe essere più semplice testarli singolarmente
Matt N

20

assertEquals(Object, Object)da JUnit4 / JUnit 5 o assertThat(actual, is(expected));da Hamcrest proposti nelle altre risposte funzioneranno solo come entrambi equals()e toString()saranno sovrascritti per le classi (e profondamente) degli oggetti confrontati.

È importante perché il test di uguaglianza nell'asserzione si basa equals()e il messaggio di fallimento del test si basa sugli toString()oggetti confrontati.
Per classi integrate come String, Integere quindi per ... nessun problema in quanto queste hanno la precedenza su entrambe equals()e toString(). Quindi è perfettamente valido per affermare List<String>o List<Integer>con assertEquals(Object,Object).
E su questo argomento: devi scavalcare equals()una classe perché ha senso in termini di uguaglianza degli oggetti, non solo per semplificare le asserzioni in un test con JUnit.
Per facilitare le asserzioni hai altri modi.
Come buona pratica preferisco le librerie di asserzioni / matcher.

Ecco una soluzione AssertJ .

org.assertj.core.api.ListAssert.containsExactly() è ciò di cui hai bisogno: verifica che il gruppo effettivo contenga esattamente i valori indicati e nient'altro, nell'ordine indicato in javadoc.

Supponi una Fooclasse in cui aggiungi elementi e dove puoi ottenerlo.
Un unit test Fooche afferma che i due elenchi hanno lo stesso contenuto potrebbe apparire come:

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 punto positivo di AssertJ è che dichiarare un Listcome previsto è inutile: rende l'asserzione più chiara e il codice più leggibile:

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

Ma le librerie Assertion / matcher sono un must perché queste lo faranno davvero oltre.
Supponiamo ora che Foo non memorizzi le istanze di Stringma Bar.
Questo è un bisogno molto comune. Con AssertJ l'affermazione è ancora semplice da scrivere. Meglio affermare che il contenuto dell'elenco è uguale anche se la classe degli elementi non ha priorità equals()/hashCode()mentre JUnit richiede che:

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

7

Se non ti interessa l'ordine degli elementi, ti consiglio ListAssert.assertEqualsin junit-addons.

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

Per gli utenti pigri di Maven:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

7
Nota: se non ti interessa l'ordine degli elementi, dovresti utilizzare un set o una raccolta, non un elenco.
Barett,

11
Sono d'accordo. Questa libreria è disgustosa. Perché mai ListAssert.assertEquals () dovrebbe essere predefinito senza ordine?
Ryan,

5

Puoi usare assertEquals in 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);
    }

Se l'ordine degli elementi è diverso, verrà restituito un errore.

Se si sta affermando un elenco di oggetti modello, è necessario sovrascrivere il metodo equals nel modello specifico.

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

4

se non si desidera creare un elenco di array, è possibile provare anche questo

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

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

1

Non reinventare la ruota!

C'è una libreria di codici Google che fa questo per te: Hamcrest

[Hamcrest] Fornisce una libreria di oggetti matcher (noti anche come vincoli o predicati) che consentono di definire in modo dichiarativo regole di 'match', da utilizzare in altri framework. Gli scenari tipici includono framework di test, librerie di simulazione e regole di convalida dell'interfaccia utente.


0

So che ci sono già molte opzioni per risolvere questo problema, ma preferirei fare quanto segue per affermare due liste in ogni caso :

assertTrue(result.containsAll(expected) && expected.containsAll(result))

questo non fallirà se l'ordine non viene abbinato ??
iec2011007,

0

assertEquals(expected, result);per me va bene. Poiché questa funzione ottiene due oggetti, puoi passarci qualsiasi cosa.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

-1

Non credo che tutte le risposte sopra riportate forniscano la soluzione esatta per confrontare due elenchi di oggetti. La maggior parte degli approcci di cui sopra può essere utile nel seguente limite di confronto - Confronto dimensioni - Confronto di riferimento

Ma se abbiamo elenchi delle stesse dimensioni di oggetti e dati diversi a livello di oggetti, questi approcci di confronto non saranno di aiuto.

Penso che il seguente approccio funzionerà perfettamente con l'override di uguale e il metodo hashcode sull'oggetto definito dall'utente.

Ho usato Xstream lib per sovrascrivere equals e hashcode ma possiamo sovrascrivere equals e hashcode anche tramite logiche / confronti vinti.

Ecco l'esempio per il tuo riferimento

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

La cosa più importante è che puoi ignorare i campi tramite Annotazione (@XStreamOmitField) se non vuoi includere alcun campo nella verifica uguale degli Oggetti. Ci sono molte Annotazioni come questa da configurare, quindi dai un'occhiata in profondità alle annotazioni di questa lib.

Sono sicuro che questa risposta ti farà risparmiare tempo per identificare l'approccio corretto per confrontare due elenchi di oggetti :). Si prega di commentare se vedi problemi su questo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.