it-swarm.com.de

Warum bietet JUnit keine assertNotEquals-Methoden an?

Weiß jemand, warum JUnit 4 assertEquals(foo,bar) aber nicht assertNotEqual(foo,bar) Methoden bereitstellt?

Es enthält assertNotSame (entsprechend assertSame) und assertFalse (entsprechend assertTrue), so dass es seltsam erscheint, dass sie sich nicht darum gekümmert haben, assertNotEqual.

Übrigens, ich weiß, dass JUnit-Addons die Methoden bereitstellen, nach denen ich suche. Ich frage nur aus Neugier.

415
Chris B

Ich würde vorschlagen, dass Sie die neueren assertThat() -Style-Zusicherungen verwenden, die auf einfache Weise alle Arten von Negationen beschreiben und automatisch eine Beschreibung dessen erstellen, was Sie erwartet haben und was Sie erhalten haben, wenn die Zusicherung fehlschlägt :

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

Alle drei Optionen sind gleichwertig. Wählen Sie die für Sie am besten lesbare Option.

Um die einfachen Namen der Methoden zu verwenden (und diese Zeitformsyntax zuzulassen), benötigen Sie die folgenden Importe:

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

In JUnit 4.11 gibt es ein assertNotEquals: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and -Annahme

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

Ich frage mich das auch. Die API von Assert ist nicht sehr symmetrisch. um zu testen, ob Objekte gleich sind, werden assertSame und assertNotSame bereitgestellt.

Natürlich ist es nicht zu lange zu schreiben:

assertFalse(foo.equals(bar));

Bei einer solchen Behauptung ist der einzige informative Teil der Ausgabe leider der Name der Testmethode, weshalb die beschreibende Meldung separat gebildet werden sollte:

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

Das ist natürlich so mühsam, dass es besser ist, selbst assertNotEqual zu würfeln. Zum Glück wird es in Zukunft vielleicht Teil der JUnit sein: JUnit-Ausgabe 22

49
Mikko Maunu

Ich würde argumentieren, dass das Fehlen von assertNotEqual in der Tat eine Asymmetrie ist und JUnit etwas weniger lernfähig macht. Beachten Sie, dass dies ein guter Fall ist, wenn Sie eine Methode hinzufügen, um die Komplexität der API zu verringern, zumindest für mich: Symmetrie hilft dabei, den größeren Raum zu regieren. Ich vermute, dass der Grund für die Auslassung sein kann, dass zu wenige Leute nach der Methode fragen. Ich erinnere mich jedoch an eine Zeit, in der es nicht einmal eine Behauptung gab. Daher habe ich die positive Erwartung, dass die Methode schließlich hinzugefügt wird, da sie nicht schwierig ist. obwohl ich anerkenne, dass es zahlreiche Workarounds gibt, auch elegante.

13

Ich komme ziemlich spät zu dieser Party, aber ich habe festgestellt, dass die Form:

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

kann für die meisten Fälle "nicht gleich" arbeiten gemacht werden.

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

Ich arbeite an JUnit in Java 8-Umgebung mit jUnit4.12

für mich: Compiler konnte die Methode assertNotEquals nicht finden, auch wenn ich verwendet habe
import org.junit.Assert;

Also habe ich mich verändert
assertNotEquals("addb", string);
zu
Assert.assertNotEquals("addb", string);

Wenn Sie also ein Problem mit assertNotEqual haben, das nicht erkannt wurde, ändern Sie es in Assert.assertNotEquals(,);. Es sollte Ihr Problem lösen

6

Der offensichtliche Grund, warum die Leute assertNotEquals () wollten, war der Vergleich von Buildins, ohne sie zuerst in vollständige Objekte konvertieren zu müssen:

Ausführliches Beispiel:

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

vs.

assertNotEqual(1, winningBidderId);

Leider müssen Sie ausführlich sein, da Eclipse standardmäßig JUnit 4.11 nicht enthält.

Vorsichtsmaßnahme Ich glaube nicht, dass die '1' in eine Integer.valueOf () eingeschlossen werden muss, aber da ich neu aus .NET zurückgekehrt bin, rechne nicht mit meiner Richtigkeit.

3
Mark Levison

Es ist besser, den Hamcrest für negative Assertionen zu verwenden, als assertFalse, da im ersteren Testbericht ein Unterschied für den Assertionsfehler angezeigt wird.

Wenn Sie assertFalse verwenden, wird im Bericht nur ein Assertionsfehler angezeigt. d.h. verlorene Information über die Fehlerursache.

1
Chris Kelly

Ich stimme voll und ganz der Sichtweise des OP zu. Assert.assertFalse(expected.equals(actual)) ist keine natürliche Art, eine Ungleichung auszudrücken.
Aber ich würde argumentieren, dass weiter als Assert.assertEquals(), Assert.assertNotEquals() funktioniert, aber nicht benutzerfreundlich ist, um zu dokumentieren, was der Test tatsächlich behauptet, und um zu verstehen/zu debuggen, wenn die Behauptung fehlschlägt.
Ja, JUnit 4.11 und JUnit 5 bieten Assert.assertNotEquals() (Assertions.assertNotEquals() in JUnit 5), aber ich vermeide es wirklich, sie zu verwenden.

Um den Zustand eines Objekts zu bestätigen, verwende ich im Allgemeinen eine Matcher-API, die sich leicht in den Objektzustand einfügt, die die Absicht der Behauptungen klar dokumentiert und die sehr benutzerfreundlich ist, um die Ursache des Behauptungsfehlers zu verstehen.

Hier ist ein Beispiel.
Angenommen, ich habe eine Animal-Klasse, mit der ich die createWithNewNameAndAge() -Methode testen möchte. Diese Methode erstellt ein neues Animal-Objekt, indem sie seinen Namen und sein Alter ändert, aber das Lieblingsessen beibehält.
Angenommen, ich verwende Assert.assertNotEquals(), um zu behaupten, dass das ursprüngliche und das neue Objekt unterschiedlich sind.
Hier ist die Animal-Klasse mit einer fehlerhaften Implementierung von 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+ (oder JUnit 5) sowohl als Test Runner als auch als Assertion Tool

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

Der Test schlägt erwartungsgemäß fehl, die dem Entwickler zur Verfügung gestellte Ursache ist jedoch nicht wirklich hilfreich. Es wird nur gesagt, dass die Werte unterschiedlich sein sollten und das toString() -Ergebnis für den aktuellen Animal -Parameter ausgegeben:

Java.lang.AssertionError: Die Werte sollten unterschiedlich sein. Tatsächlich: Tier

[Name = Scoubi, Alter = 10, Lieblingsessen = Heu]

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

Ok, die Objekte sind nicht gleich. Aber wo liegt das Problem?
Welches Feld wird in der getesteten Methode nicht korrekt bewertet? Ein ? Zwei? Alle von ihnen ?
Um es herauszufinden, müssen Sie in der createWithNewNameAndAge()-Implementierung einen Debugger verwenden, während die Test-API viel benutzerfreundlicher wäre, wenn sie für uns den Unterschied zwischen dem, was erwartet wird und dem, was ist, ausmachen würde bekommen.


JUnit 4.11 als Testrunner und eine Test Matcher API als Assertion Tool

Hier dasselbe Test-Szenario, das jedoch AssertJ (eine hervorragende Test-Matcher-API) verwendet, um die Behauptung des Animal -Status zu erstellen:

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

Natürlich schlägt der Test immer noch fehl, aber diesmal ist der Grund klar angegeben:

Java.lang.AssertionError:

Erwarten:

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

genau enthalten (und in der gleichen Reihenfolge):

<["kleiner Scoubi", 1, "Heu"]>

einige Elemente wurden jedoch nicht gefunden:

<["kleiner Scoubi", 1]>

und andere wurden nicht erwartet:

<["scoubi", 10]>

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

Wir können lesen, dass wir für Animal::getName, Animal::getAge, Animal::getFavoriteFood - Werte des zurückgegebenen Tieres diesen Wert erwarten:

"little scoubi", 1, "hay" 

aber wir hatten diese Werte:

"scoubi", 10, "hay"

Wir wissen also, wo untersucht wird: name und age sind nicht richtig bewertet. Darüber hinaus ermöglicht die Angabe des hay-Werts in der Zusicherung von Animal::getFavoriteFood() auch eine genauere Zusicherung des zurückgegebenen Animal. Wir möchten, dass die Objekte für einige Eigenschaften nicht identisch sind, aber nicht unbedingt für alle Eigenschaften.
Auf jeden Fall ist die Verwendung einer Matcher-API viel klarer und flexibler.

0
davidxxx