web-dev-qa-db-fra.com

Pourquoi JUnit ne fournit-il pas les méthodes assertNotEquals?

Quelqu'un sait-il pourquoi JUnit 4 fournit des méthodes assertEquals(foo,bar) mais pas assertNotEqual(foo,bar)?

Il fournit assertNotSame (correspondant à assertSame) et assertFalse (correspondant à assertTrue), il semble donc étrange qu'ils n'aient pas pris la peine d'inclure assertNotEqual.

En passant, je sais que JUnit-addons fournit les méthodes que je recherche. Je demande juste par curiosité.

415
Chris B

Je suggérerais que vous utilisiez les assertions de style les plus récentes assertThat() , qui peuvent facilement décrire toutes sortes de négations et construire automatiquement une description de ce à quoi vous vous attendiez et de ce que vous obteniez si l'assertion échouait:

assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));

Les trois options sont équivalentes, choisissez celle que vous trouvez le plus lisible.

Pour utiliser les noms simples des méthodes (et permettre à cette syntaxe de temps de fonctionner), vous avez besoin des importations suivantes:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
394
Joachim Sauer

Il y a une assertNotEquals dans JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and- assumer

import static org.junit.Assert.assertNotEquals;
149
Stefan Birkner

Je me demande même. L'API de Assert n'est pas très symétrique; pour tester si les objets sont identiques, il fournit assertSame et assertNotSame.

Bien sûr, il n’est pas trop long d’écrire:

assertFalse(foo.equals(bar));

Avec une telle assertion, la seule partie informative de la sortie est malheureusement le nom de la méthode de test. Par conséquent, le message descriptif doit être formé séparément:

String msg = "Expected <" + foo + "> to be unequal to <" + bar +">";
assertFalse(msg, foo.equals(bar));

C'est bien sûr fastidieux qu'il vaut mieux rouler votre propre assertNotEqual. Heureusement, à l’avenir, il fera peut-être partie de JUnit: JUnit numéro 22

49
Mikko Maunu

Je dirais que l'absence d'assertNotEqual est effectivement une asymétrie et rend JUnit un peu moins compréhensible. N'oubliez pas qu'il s'agit d'un cas pratique où l'ajout d'une méthode diminuerait la complexité de l'API, du moins pour moi: Symmetry permet de contrôler l'espace plus grand. J'imagine que la raison de l'omission est peut-être qu'il y a trop peu de personnes qui demandent la méthode. Pourtant, je me souviens d'une époque où même l'assertFalse n'existait pas; par conséquent, j’espère positivement que la méthode pourra éventuellement être ajoutée, étant donné qu’elle n’est pas difficile; Même si je reconnais qu'il existe de nombreuses solutions de contournement, même élégantes.

13

J'arrive assez tard à cette soirée mais j'ai trouvé que le formulaire:

static void assertTrue(Java.lang.String message, boolean condition) 

peut être fait fonctionner pour la plupart des cas "pas égaux".

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
7
user903724

Je travaille sur JUnit dans l'environnement Java 8, à l'aide de jUnit4.12

pour moi: le compilateur n'a pas pu trouver la méthode assertNotEquals, même lorsque j'ai utilisé
import org.junit.Assert;

Alors j'ai changé
assertNotEquals("addb", string);
à
Assert.assertNotEquals("addb", string);

Donc, si vous rencontrez un problème concernant assertNotEqual non reconnu, changez-le en Assert.assertNotEquals(,); cela devrait résoudre votre problème

6

La raison évidente pour laquelle les gens voulaient assertNotEquals () était de comparer les éléments intégrés sans avoir à les convertir au préalable en objets complets:

Exemple verbeux:

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

vs.

assertNotEqual(1, winningBidderId);

Malheureusement, Eclipse n'incluant pas JUnit 4.11 par défaut, vous devez être prolixe.

Mise en garde Je ne pense pas que le '1' doive être enveloppé dans un Integer.valueOf () mais comme je viens de rentrer de .NET, ne comptez pas sur mon exactitude.

3
Mark Levison

Il vaut mieux utiliser Hamcrest pour les assertions négatives plutôt que pour assertFalse, car dans le premier cas, le rapport de test indiquait un diff pour l'échec de l'assertion.

Si vous utilisez assertFalse, vous obtenez simplement un échec d'assertion dans le rapport. c'est-à-dire la perte d'informations sur la cause de la défaillance.

1
Chris Kelly

Je suis totalement d'accord avec le point de vue de l'OP. Assert.assertFalse(expected.equals(actual)) n'est pas un moyen naturel d'exprimer une inégalité.
Mais j’arguerais que plus que Assert.assertEquals(), Assert.assertNotEquals() ne fonctionne pas mais qu’il n’est pas convivial de documenter ce que le test affirme réellement et de comprendre/déboguer lorsque l’assertion échoue.
Donc, oui, JUnit 4.11 et JUnit 5 fournissent Assert.assertNotEquals() (Assertions.assertNotEquals() dans JUnit 5) mais j’évite vraiment de les utiliser.

En guise d'alternative, pour affirmer l'état d'un objet, j'utilise généralement une API matcher qui creuse facilement dans l'état de l'objet, ce document indique clairement l'intention des assertions et facilite grandement la compréhension de la cause de l'échec de l'assertion.

Voici un exemple.
Supposons que je souhaite tester la méthode createWithNewNameAndAge() dans la classe Animal. Cette méthode permet de créer un nouvel objet Animal en changeant son nom et son âge, mais en conservant son aliment préféré.
Supposons que j'utilise Assert.assertNotEquals() pour affirmer que l'original et les nouveaux objets sont différents.
Voici la classe Animal avec une implémentation imparfaite de createWithNewNameAndAge():

public class Animal {

    private String name;
    private int age;
    private String favoriteFood;

    public Animal(String name, int age, String favoriteFood) {
        this.name = name;
        this.age = age;
        this.favoriteFood = favoriteFood;
    }

    // Flawed implementation : use this.name and this.age to create the 
    // new Animal instead of using the name and age parameters
    public Animal createWithNewNameAndAge(String name, int age) {
        return new Animal(this.name, this.age, this.favoriteFood);
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getFavoriteFood() {
        return favoriteFood;
    }

    @Override
    public String toString() {
        return "Animal [name=" + name + ", age=" + age + ", favoriteFood=" + favoriteFood + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((favoriteFood == null) ? 0 : favoriteFood.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Animal)) return false;

        Animal other = (Animal) obj;
        return age == other.age && favoriteFood.equals(other.favoriteFood) &&
                name.equals(other.name);
    }

}

JUnit 4.11+ (ou JUnit 5) à la fois comme exécuteur de test et outil d’assertion

@Test
void assertListNotEquals_JUnit_way() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assert.assertNotEquals(scoubi, littleScoubi);
}

Le test échoue comme prévu, mais la cause fournie au développeur n’est vraiment pas utile. Il indique simplement que les valeurs doivent être différentes et affiche le résultat toString() appelé dans le paramètre réel Animal:

Java.lang.AssertionError: les valeurs doivent être différentes. Réel: Animal

[nom = scoubi, age = 10, favoriFood = foin]

à org.junit.Assert.fail (Assert.Java:88)

Ok les objets ne sont pas égaux. Mais où est le problème?
Quel champ n’est pas correctement évalué dans la méthode testée? Un ? Deux ? Tous ?
Pour le découvrir, vous devez creuser dans la createWithNewNameAndAge() implémentation/utiliser un débogueur alors que l'API de test serait beaucoup plus conviviale si elle nous permettait de faire la différence entre ce qui est attendu et ce qui est obtenu.


JUnit 4.11 en tant que lanceur de test et une API de test Matcher en tant qu'outil d'assertion

Voici le même scénario de test, mais qui utilise AssertJ (une excellente API de test matcher) pour effectuer l’affirmation de l’état Animal:

import org.assertj.core.api.Assertions;

@Test
void assertListNotEquals_AssertJ() {
    Animal scoubi = new Animal("scoubi", 10, "hay");
    Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
    Assertions.assertThat(littleScoubi)
              .extracting(Animal::getName, Animal::getAge, Animal::getFavoriteFood)
              .containsExactly("little scoubi", 1, "hay");
}

Bien sûr, le test échoue mais cette fois, la raison est clairement indiquée:

Java.lang.AssertionError:

Attendant:

<["scoubi", 10, "foin"]>

contenir exactement (et dans le même ordre):

<["petite scoubi", 1, "foin"]>

mais certains éléments n'ont pas été trouvés:

<["petite scoubi", 1]>

et d'autres n'étaient pas attendus:

<["scoubi", 10]>

à junit5.MyTest.assertListNotEquals_AssertJ (MyTest.Java:26)

Nous pouvons lire que pour Animal::getName, Animal::getAge, Animal::getFavoriteFood les valeurs de l'Animal retourné, nous nous attendons à avoir les valeurs suivantes:

"little scoubi", 1, "hay" 

mais nous avons eu ces valeurs:

"scoubi", 10, "hay"

Nous savons donc où enquêter: name et age ne sont pas correctement évalués. De plus, le fait de spécifier la valeur hay dans l'assertion de Animal::getFavoriteFood() permet également d'affiner plus finement le Animal renvoyé. Nous voulons que les objets ne soient pas les mêmes pour certaines propriétés mais pas nécessairement pour toutes les propriétés.
Donc, utiliser une API matcher est bien plus clair et flexible.

0
davidxxx