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

429

Est-ce que quelqu'un sait pourquoi JUnit 4 fournit assertEquals(foo,bar)mais pas des assertNotEqual(foo,bar)méthodes?

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

Soit dit en passant, je sais que les add-ons JUnit fournissent les méthodes que je recherche. Je demande juste par curiosité.

Chris B
la source
Au moins depuis JUnit 4.12, assertNotEquals est fourni. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Réponses:

403

Je vous suggère d'utiliser les assertions de assertThat()style plus récentes , qui peuvent facilement décrire toutes sortes de négations et créer automatiquement une description de ce que vous attendez et de ce que vous obtenez si l'assertion échoue:

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

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

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

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
Joachim Sauer
la source
134
J'apprécie le pointeur vers la syntaxe d'assertion alternative, mais pointer ailleurs ne répond pas pourquoi JUnit n'a jamais fourni assertNotEquals().
SEH
14
@seh: D'après ma lecture, la question ne portait pas sur un intérêt historique, mais sur un moyen de formuler l'affirmation "ces deux objets ne sont pas égaux" dans un test JUnit. J'ai répondu à ça. Compte tenu du «pourquoi est / n'y a-t-il pas assertNotEqual», je dirais que c'est parce que c'est une affirmation spécialisée qui n'est pas nécessaire aussi souvent assertEqualset qui serait donc exprimée via le générique assertFalse.
Joachim Sauer
21
"choisissez celui que vous trouvez le plus lisible". Les personnes qui lisent et écrivent des tests unitaires sont des programmeurs. Trouvent-ils vraiment cela plus lisible que assertNotEqual (objectUnderTest, someOtherObject) ou assertFalse (objectUnderTest.equals (someOtherObject))? Je ne suis pas convaincu par les API de matcher fantaisie - il semble qu'il soit beaucoup plus difficile pour un programmeur d'explorer / découvrir comment les utiliser ...
bacar
@bacar: pour certains, c'est essentiellement une question de style. Mais assertThatest beaucoup plus expressif que l'ensemble limité de assert*méthodes disponibles. Par conséquent, vous pouvez exprimer les contraintes exactes sur une seule ligne, la lire (presque) comme une phrase en anglais et obtenir un message significatif lorsque l'assertion échoue. Certes, ce n'est pas toujours une fonctionnalité qui tue, mais quand vous l'avez vue en action à plusieurs reprises, vous verrez combien elle ajoute de la valeur.
Joachim Sauer
5
@Joachim Je suis d'accord pour dire que assertThatc'est plus expressif que assert*, mais je ne pense pas que ce soit plus expressif que l'expression java que vous pouvez mettre à l'intérieur et à l'extérieur de l' assert*expression en général (après tout, je peux tout exprimer en code java). C'est un problème général que j'ai commencé à rencontrer avec des API de style fluide - chacune est fondamentalement une nouvelle DSL que vous devez apprendre (quand nous connaissons déjà la Java!). Je suppose que Hamcrest est suffisamment omniprésent maintenant qu'il est raisonnable de s'attendre à ce que les gens le sachent. Je vais jouer ...
bacar
154

Il y en a assertNotEqualsdans JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume

import static org.junit.Assert.assertNotEquals;
Stefan Birkner
la source
1
Attention, l'un des artefacts jmock (2.6.0) maven laisse échapper une ancienne version de junit-dep qui à son tour a une classe Assert sans assertNotEquals. Mieux vaut exclure cela lors de l'utilisation de lierre.
gkephorus
7
J'utilise 4.12 mais je ne trouve toujours pas assertNotEqual. : s
Mubashar
49

Je me demande la même chose. L'API d'Assert n'est pas très symétrique; pour tester si les objets sont les mêmes, il fournit assertSameet assertNotSame.

Bien sûr, il n'est pas trop long pour écrire:

assertFalse(foo.equals(bar));

Avec une telle affirmation, la seule partie informative de la sortie est malheureusement le nom de la méthode de test, donc 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 si fastidieux, qu'il vaut mieux rouler le vôtre assertNotEqual. Heureusement, à l'avenir, il fera peut-être partie du JUnit: JUnit numéro 22

Mikko Maunu
la source
19
Mais cela est moins utile, car JUnit ne peut pas générer un message d'échec utile vous indiquant, par exemple, les valeurs inégales de foo et bar. La véritable raison de l'échec est cachée et transformée en un simple booléen.
Ben James
3
Je suis entièrement d'accord. En particulier, assertFalse a besoin d'un argument de message approprié pour produire une sortie indiquant ce qui s'est vraiment passé.
Mikko Maunu
Je pense que cela est utile pour les tests de texte. Thnx
Marouane Gazanayi
Le problème avec le texte est qu'il sera obsolète à mesure que le code évolue.
Mark Levison
13

Je dirais que l'absence de assertNotEqual est en effet une asymétrie et rend JUnit un peu moins apprenable. Gardez à l'esprit qu'il s'agit d'un cas intéressant lorsque l'ajout d'une méthode réduirait la complexité de l'API, du moins pour moi: la symétrie permet de gouverner le plus grand espace. Je suppose que la raison de l'omission est peut-être qu'il y a trop peu de gens qui demandent la méthode. Pourtant, je me souviens d'une époque où même affirmerFaux n'existait pas; par conséquent, j'ai une attente positive que la méthode puisse é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.

Bernhard Bodenstorfer
la source
7

Je viens à cette fête assez tard mais j'ai trouvé que le formulaire:

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

peut fonctionner pour la plupart des cas «différents».

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
user903724
la source
4
Bien que cela fonctionne, le problème est que si l'assertion échoue, elle indiquera simplement "Excepté vrai, mais était faux", ou une autre déclaration peu claire. Ce qui serait génial, c'est que c'était prévu Pas 123, mais 123.
Stealth Rabbi
6

Je travaille sur JUnit dans un environnement java 8, en utilisant 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;

J'ai donc changé
assertNotEquals("addb", string);
pour
Assert.assertNotEquals("addb", string);

Donc, si vous rencontrez un problème concernant assertNotEqualnon reconnu, alors changez-le pour Assert.assertNotEquals(,);résoudre votre problème

Akshay Vijay Jain
la source
1
En effet, les méthodes sont statiques et vous devez les importer de manière statique. Utilisez-le import static org.junit.Assert.*;et vous pourrez utiliser toutes les assertions sans le nom de la classe.
Tom Stone
3

La raison évidente pour laquelle les gens voulaient assertNotEquals () était de comparer les buildins sans avoir à les convertir en objets à part entière:

Exemple détaillé:

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

contre.

assertNotEqual(1, winningBidderId);

Malheureusement, comme Eclipse n'inclut pas JUnit 4.11 par défaut, vous devez être bavard.

Attention, je ne pense pas que le «1» doive être enveloppé dans un Integer.valueOf () mais puisque je suis récemment revenu de .NET, ne comptez pas sur mon exactitude.

Mark Levison
la source
1

Il est préférable d'utiliser Hamcrest pour les assertions négatives plutôt que assertFalse car dans le premier, le rapport de test montrera 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 des informations perdues sur la cause de l'échec.

Chris Kelly
la source
1

Habituellement, je fais cela lorsque je m'attends à ce que deux objets soient égaux:

assertTrue(obj1.equals(obj2));

et:

assertFalse(obj1.equals(obj2));

quand ils devraient être inégaux. Je suis conscient que ce n'est pas une réponse à votre question mais c'est le plus proche que je peux obtenir. Cela pourrait aider les autres à rechercher ce qu'ils peuvent faire dans les versions de JUnit avant JUnit 4.11.

Willempie
la source
0

Je suis totalement d'accord avec le point de vue OP. Assert.assertFalse(expected.equals(actual))n'est pas un moyen naturel d'exprimer une inégalité.
Mais je dirais que plus que Assert.assertEquals(), les Assert.assertNotEquals()œuvres , mais n'est pas facile à utiliser pour documenter ce que le test affirme en fait et de comprendre / debug que 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 de correspondance qui creuse facilement dans l'état de l'objet, qui documente clairement l'intention des assertions et qui est très conviviale pour comprendre la cause de l'échec de l'assertion.

Voici un exemple.
Supposons que j'ai une classe Animal dont je veux tester la createWithNewNameAndAge()méthode, une méthode qui crée un nouvel objet Animal en changeant son nom et son âge mais en gardant sa nourriture préférée.
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 lanceur 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 dit simplement que les valeurs doivent être différentes et produire le toString()résultat appelé sur le Animalparamètre réel :

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

[name = scoubi, age = 10, favoriteFood = hay]

à 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? Une ? Deux ? Tous ?
Pour le découvrir, vous devez creuser dans l' createWithNewNameAndAge() implémentation / utiliser un débogueur tandis que l'API de test serait beaucoup plus conviviale si elle faisait pour nous le différentiel entre ce qui est attendu et ce qui est obtenu.


JUnit 4.11 en tant que lanceur de test et une API Matcher de test 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 faire l'affirmation de l' Animalétat::

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 toujours, mais cette fois, la raison est clairement indiquée:

java.lang.AssertionError:

Attendant:

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

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

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

mais certains éléments sont introuvables:

<["petit scoubi", 1]>

et d'autres n'étaient pas attendus:

<["scoubi", 10]>

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

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

"little scoubi", 1, "hay" 

mais nous avons eu ces valeurs:

"scoubi", 10, "hay"

Nous savons donc où enquêter: nameet agene sont pas correctement évalués. De plus, le fait de spécifier la hayvaleur dans l'assertion de Animal::getFavoriteFood()permet également d'affirmer plus finement le retour Animal. 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, certainement, l'utilisation d'une API Matcher est beaucoup plus claire et flexible.

davidxxx
la source
-1

La cohérence de l'API Modulo, pourquoi JUnit n'a pas fourni, assertNotEquals()c'est la même raison pour laquelle JUnit n'a jamais fourni de méthodes comme

  • assertStringMatchesTheRegex(regex, str) contre. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) contre. assertStringDoesntBeginWith(prefix, str)

c'est-à-dire qu'il n'y a pas de fin à fournir des méthodes d'assertion spécifiques pour le genre de choses que vous pourriez souhaiter dans votre logique d'assertion!

Il est préférable de fournir des primitives de test composables comme equalTo(...),is(...) , not(...), regex(...)et laisser la pièce de programmeur les ensemble au lieu pour plus de lisibilité et de santé mentale.

fatuhoku
la source
3
eh bien, pour une raison quelconque, assertEquals () existe. Ce n'était pas nécessaire, mais c'est le cas. La question portait sur le manque de symétrie - pourquoi assertEquals existe-t-il mais pas son homologue?
foo