web-dev-qa-db-fra.com

Affirmation à propos d'une liste dans Junit

Comment faire des affirmations sur une liste dans un cas de testJUnit? Non seulement la taille de la liste mais aussi le contenu de la liste.

127
Kamal

Je me rends compte que cela a été demandé il y a quelques années. Cette fonctionnalité n'était probablement pas disponible à l'époque. Mais maintenant, il est facile de faire ceci:

@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 vous avez une version récente de Junit installée avec hamcrest, ajoutez simplement ces importations:

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

135
djeikyb

Il s’agit d’une réponse classique, adaptée à JUnit 4.3 et inférieur. La version moderne de JUnit inclut des messages d'échec lisibles intégrés dans la méthode assertThat. Préférez les autres réponses à cette question, si possible.

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

Pour mémoire, comme le mentionnait @Paul dans son commentaire sur cette réponse, deux Lists sont égaux:

if et uniquement si l'objet spécifié est également une liste, les deux listes ont la même taille et toutes les paires d'éléments correspondantes dans les deux listes sont égales. (Deux éléments e1 et e2 sont égaux si (e1==null ? e2==null : e1.equals(e2)).) En d'autres termes, deux listes sont définies comme étant égales si elles contiennent les mêmes éléments dans le même ordre. Cette définition garantit que la méthode equals fonctionne correctement dans différentes implémentations de l'interface List.

Voir le JavaDocs de l'interface List .

19
Bart Kiers

Ne pas transformer en chaîne et comparer. Ce n'est pas bon pour la performance. Dans le Junit, dans Corematchers, il y a un indicateur pour ceci => hasItems

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

C'est le meilleur moyen que je connaisse pour vérifier les éléments d'une liste.

18
Andre Fonseca

Si vous ne vous souciez pas de l'ordre des éléments, je recommande ListAssert.assertEquals dans junit-addons.

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

Pour les utilisateurs paresseux de Maven:

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

assertEquals(Object, Object) de JUnit4/JUnit 5 ou assertThat(actual, is(expected)); de Hamcrest proposé dans les autres réponses ne fonctionnera que si equals() et toString() sont remplacés pour les classes (et profondément) des objets comparés. 

Cela est important car le test d'égalité dans l'assertion repose sur equals() et que le message d'échec de test repose sur toString() des objets comparés.
Pour les classes intégrées telles que String, Integer et ainsi de suite ... pas de problème, car elles ont priorité sur equals() et toString(). Il est donc parfaitement correct d'affirmer List<String> ou List<Integer> avec assertEquals(Object,Object).
Et sur ce point: vous devez remplacer equals() dans une classe car cela a du sens en termes d’égalité d’objet, pas seulement pour faciliter les assertions dans un test avec JUnit.
Pour faciliter les assertions, vous avez d'autres moyens.
En tant que bonne pratique, je préfère les bibliothèques d’affirmations/matcher. 

Voici un AssertJ solution. 

org.assertj.core.api.ListAssert.containsExactly() est ce dont vous avez besoin: il vérifie que le groupe actuel contient exactement les valeurs données et rien d'autre, dans l'ordre indiqué dans le javadoc. 

Supposons une classe Foo où vous ajoutez des éléments et où vous pouvez l'obtenir.
Un test unitaire de Foo qui affirme que les deux listes ont le même contenu pourrait ressembler à ceci:

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 point positif de AssertJ est que déclarer une List comme prévu est inutile: cela rend l'assertion plus claire et le code plus lisible:

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

Mais les bibliothèques Assertion/Matcher sont indispensables car elles vont vraiment plus loin.
Supposons maintenant que Foo ne stocke pas Strings mais Bars instances.
C'est un besoin très commun. Avec AssertJ, l’affirmation reste simple à écrire. Mieux vous pouvez affirmer que le contenu de la liste est égal même si la classe des éléments ne remplace pas equals()/hashCode() alors que JUnit exige 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"));
}
4
davidxxx

si vous ne voulez pas construire une liste de tableaux, vous pouvez aussi essayer ceci 

@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
Arun Pratap Singh
List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));
3
Ruslan Gafiullin

Vous pouvez utiliser 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);
    }

Si l'ordre des éléments est différent, une erreur sera renvoyée.

Si vous revendiquez une liste d'objets de modèle, vous devriez alors remplacer la méthode equals dans le modèle spécifique.

    @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;
    }
2
Dhyan Mohandas

Ne réinventez pas la roue!

Une bibliothèque de codes Google le fait pour vous: Hamcrest

[Hamcrest] Fournit une bibliothèque d'objets de correspondance (également appelés contraintes ou prédicats) permettant aux règles de "correspondance" d'être définies de manière déclarative, afin d'être utilisées dans d'autres cadres. Les scénarios typiques incluent des frameworks de test, des bibliothèques mocking et des règles de validation de l'interface utilisateur.

1
Bohemian

Toutes les réponses ci-dessus donnent la solution exacte pour comparer deux listes d'objets . La plupart des approches ci-dessus peuvent être utiles uniquement pour la suite des comparaisons suivantes.

Mais si nous avons des listes d'objets de même taille et des données différentes au niveau des objets, cette approche de comparaison ne sera d'aucune utilité. 

Je pense que l'approche suivante fonctionnera parfaitement avec le remplacement des méthodes equals et hashcode sur l'objet défini par l'utilisateur.

J'ai utilisé Xstream lib pour remplacer les valeurs égales et hashcode, mais nous pouvons également remplacer les valeurs égales et hashcode par la logique/comparaison gagnée.

Voici l'exemple pour votre référence

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

Le plus important est que vous pouvez ignorer les champs par annotation (@XStreamOmitField) si vous ne souhaitez pas inclure de champs dans la vérification égale des objets. Il y a beaucoup d'annotations comme celle-ci à configurer alors jetez un œil aux annotations de cette bibliothèque.

Je suis sûr que cette réponse vous fera gagner du temps pour identifier la bonne approche pour comparer deux listes d'objets :). Veuillez commenter si vous rencontrez des problèmes à ce sujet.

0