Warum bietet JUnit keine assertNotEquals-Methoden an?

429

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

Es bietet assertNotSame(entsprechend assertSame) und assertFalse(entsprechend assertTrue), so dass es seltsam erscheint, dass sie sich nicht die Mühe gemacht haben, einzuschließen assertNotEqual.

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

Chris B.
quelle
Spätestens seit JUnit 4.12 wird assertNotEquals bereitgestellt. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Antworten:

403

Ich würde vorschlagen, dass Sie die neueren assertThat()Stil-Asserts verwenden, die alle Arten von Negationen leicht beschreiben und automatisch eine Beschreibung dessen erstellen können, was Sie erwartet haben und was Sie erhalten haben, wenn die Assertion 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 Option aus, die Sie am besten lesen können.

Um die einfachen Namen der Methoden zu verwenden (und diese angespannte Syntax funktionieren zu lassen), benötigen Sie diese Importe:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
Joachim Sauer
quelle
134
Ich schätze den Zeiger auf die alternative Assertionssyntax, aber das Zeigen auf eine andere Stelle beantwortet nicht, warum JUnit nie bereitgestellt hat assertNotEquals().
seh
14
@seh: So wie ich es gelesen habe, ging es bei der Frage nicht um historisches Interesse, sondern um eine Möglichkeit, die Behauptung "Diese beiden Objekte sind nicht gleich" in einem JUnit-Test zu formulieren. Ich habe das beantwortet. In Anbetracht des "Warum gibt / gab es kein assertNotEqual" würde ich sagen, dass dies eine spezialisierte Behauptung ist, die nicht so oft benötigt wird assertEqualsund daher über das Generikum ausgedrückt wird assertFalse.
Joachim Sauer
21
"Wählen Sie diejenige aus, die Sie am besten lesen können". Menschen, die Unit-Tests lesen und schreiben, sind Programmierer. Finden sie das wirklich lesbarer als assertNotEqual (objectUnderTest, someOtherObject) oder assertFalse (objectUnderTest.equals (someOtherObject))? Ich bin nicht überzeugt von den ausgefallenen Matcher-APIs - es scheint für einen Programmierer erheblich schwieriger zu sein, zu erforschen / zu entdecken, wie man sie verwendet ...
Bacar
@bacar: Für einige Behauptungen ist es im Grunde eine Frage des Stils. Aber es assertThatist viel ausdrucksvoller als die begrenzten assert*verfügbaren Methoden. Daher können Sie die genauen Einschränkungen in einer einzigen Zeile ausdrücken, sie (fast) wie einen englischen Satz lesen lassen und eine aussagekräftige Nachricht erhalten, wenn die Bestätigung fehlschlägt. Zugegeben, das ist nicht immer ein Killer-Feature, aber wenn Sie es ein paar Mal in Aktion gesehen haben, werden Sie sehen, wie viel Wert es hinzufügt.
Joachim Sauer
5
@Joachim Ich stimme zu, dass dies assertThatausdrucksvoller ist als assert*, aber ich denke nicht, dass es ausdrucksvoller ist als der Java-Ausdruck, den Sie assert*im Allgemeinen in den Ausdruck hinein- und herausfügen können (schließlich kann ich alles in Java-Code ausdrücken). Es ist ein allgemeines Problem, auf das ich bei fließenden APIs gestoßen bin - jedes ist im Grunde ein neues DSL, das Sie lernen müssen (wenn wir alle bereits das Java kennen!). Ich nehme an, Hamcrest ist jetzt allgegenwärtig genug, da es vernünftig ist zu erwarten, dass die Leute es wissen. Ich werde ein Stück spielen ...
Bacar
154

assertNotEqualsIn JUnit 4.11 gibt es eine : 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
quelle
1
Beachten Sie, dass eines der Maven-Artefakte von jmock (2.6.0) eine alte Version von junit-dep verliert, die wiederum eine Assert-Klasse ohne assertNotEquals hat. Schließen Sie dies besser aus, wenn Sie Efeu verwenden.
Gkephorus
7
Ich verwende 4.12, kann jedoch assertNotEqual immer noch nicht finden. : s
Mubashar
49

Ich frage mich das auch. Die API von Assert ist nicht sehr symmetrisch. Zum Testen, ob Objekte identisch sind, wird Folgendes bereitgestelltassertSame undassertNotSame .

Natürlich ist es nicht zu lang zu schreiben:

assertFalse(foo.equals(bar));

Bei einer solchen Behauptung ist der einzige informative Teil der Ausgabe leider der Name der Testmethode, daher sollte eine beschreibende Nachricht separat gebildet werden:

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

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

Mikko Maunu
quelle
19
Dies ist jedoch weniger nützlich, da JUnit keine hilfreiche Fehlermeldung generieren kann, die Sie beispielsweise über die ungleichen Werte von foo und bar informiert. Der wahre Fehlergrund ist verborgen und wird in einen einfachen Booleschen Wert umgewandelt.
Ben James
3
Ich bin vollkommen einverstanden. Insbesondere assertFalse benötigt ein geeignetes Nachrichtenargument, um eine Ausgabe zu erstellen, mit der festgestellt werden kann, was wirklich schief gelaufen ist.
Mikko Maunu
Ich denke, dies ist nützlich für die vorliegenden Tests. Danke
Marouane Gazanayi
Das Problem mit dem Text ist, dass er veraltet ist, wenn sich der Code weiterentwickelt.
Mark Levison
13

Ich würde argumentieren, dass das Fehlen von assertNotEqual in der Tat eine Asymmetrie darstellt und JUnit etwas weniger lernbar macht. Beachten Sie, dass dies ein guter Fall ist, wenn das Hinzufügen einer Methode die Komplexität der API verringern würde, zumindest für mich: Symmetrie hilft dabei, den größeren Raum zu regieren. Ich vermute, dass der Grund für die Auslassung darin liegen könnte, dass zu wenige Leute nach der Methode rufen. Ich erinnere mich jedoch an eine Zeit, als es nicht einmal AssertFalse gab. Daher habe ich eine positive Erwartung, dass die Methode irgendwann hinzugefügt werden könnte, da es keine schwierige ist. obwohl ich anerkenne, dass es zahlreiche Problemumgehungen gibt, sogar elegante.

Bernhard Bodenstorfer
quelle
7

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 "nicht gleich" Fälle gemacht werden.

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
user903724
quelle
4
Während dies funktioniert, besteht das Problem darin, dass, wenn die Behauptung fehlschlägt, einfach "Erwartet wahr, aber falsch" oder eine andere unklare Aussage angezeigt wird. Was großartig wäre, wäre, wenn es nicht 123, sondern 123 erwartet würde.
Stealth Rabbi
6

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

Für mich: Der Compiler konnte die Methode assertNotEquals nicht finden, selbst wenn ich sie verwendet habe
import org.junit.Assert;

Also wechselte ich
assertNotEquals("addb", string);
zu
Assert.assertNotEquals("addb", string);

Wenn Sie also auf ein Problem stoßen, das assertNotEqualnicht erkannt wurde, ändern Sie Assert.assertNotEquals(,);es in, um Ihr Problem zu lösen

Akshay Vijay Jain
quelle
1
Dies liegt daran, dass die Methoden statisch sind und Sie sie statisch importieren müssen. Verwenden Sie diese Option, import static org.junit.Assert.*;und Sie können alle Asserts ohne den Klassennamen verwenden.
Tom Stone
3

Der offensichtliche Grund, warum Leute assertNotEquals () wollten, war, eingebaute Objekte zu vergleichen, 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);

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

Vorsichtsmaßnahme Ich glaube nicht, dass die '1' in eine Integer.valueOf () eingeschlossen werden muss, aber da ich neu von .NET zurückgekehrt bin, kann ich nicht auf meine Richtigkeit zählen.

Mark Levison
quelle
1

Es ist besser, das Hamcrest für negative Behauptungen zu verwenden, als assertFalse, da im ersten Bericht der Testbericht einen Unterschied für den Assertionsfehler zeigt.

Wenn Sie assertFalse verwenden, wird im Bericht nur ein Assertionsfehler angezeigt. dh verlorene Informationen über die Fehlerursache.

Chris Kelly
quelle
1

Normalerweise mache ich das, wenn ich erwarte, dass zwei Objekte gleich sind:

assertTrue(obj1.equals(obj2));

und:

assertFalse(obj1.equals(obj2));

wenn erwartet wird, dass sie ungleich sind. Mir ist bewusst, dass dies keine Antwort auf Ihre Frage ist, aber es ist die nächste, die ich bekommen kann. Dies könnte anderen bei der Suche nach Möglichkeiten in JUnit-Versionen vor JUnit 4.11 helfen.

Willempie
quelle
0

Ich stimme dem OP-Standpunkt voll und ganz zu. Assert.assertFalse(expected.equals(actual))ist kein natürlicher Weg, um 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.
Also 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 Status eines Objekts zu bestätigen, verwende ich im Allgemeinen eine Matcher-API, die sich leicht in den Objektstatus einfügt, die Absicht der Behauptungen klar dokumentiert und die sehr benutzerfreundlich ist, um die Ursache des Assertionsfehlers zu verstehen.

Hier ist ein Beispiel.
Angenommen, ich habe eine Tierklasse, in der ich die createWithNewNameAndAge()Methode testen möchte. Diese Methode erstellt ein neues Tierobjekt, indem es seinen Namen und sein Alter ändert, aber sein Lieblingsfutter beibehält.
Angenommen, ich verwende Assert.assertNotEquals(), um zu behaupten, dass das ursprüngliche und das neue Objekt unterschiedlich sind.
Hier ist die Tierklasse 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 Testläufer 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 wie erwartet fehl, aber die dem Entwickler angegebene Ursache ist wirklich nicht hilfreich. Es heißt nur, dass die Werte unterschiedlich sein sollten und das toString()Ergebnis ausgeben sollten, das für den tatsächlichen AnimalParameter aufgerufen wurde :

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

[Name = Scoubi, Alter = 10, FavoritFutter = 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 richtig bewertet? Eins ? Zwei? Alle von ihnen ?
Um es zu entdecken, müssen Sie in der createWithNewNameAndAge() Implementierung graben / einen Debugger verwenden, während die Test-API viel freundlicher wäre, wenn sie für uns den Unterschied zwischen dem, was erwartet wird und dem, der erhalten wird, ausmachen würde.


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

Hier das gleiche Testszenario, das jedoch AssertJ (eine hervorragende Test-Matcher-API) verwendet, um die Aussage über den AnimalStatus zu treffen ::

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 (und in derselben Reihenfolge) enthalten:

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

Einige Elemente wurden jedoch nicht gefunden:

<["kleiner Scoubi", 1]>

und andere wurden nicht erwartet:

<["scoubi", 10]>

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

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

"little scoubi", 1, "hay" 

aber wir haben diese Werte gehabt:

"scoubi", 10, "hay"

Wir wissen also, wo untersucht wird: nameund agewerden nicht richtig bewertet. Darüber hinaus ermöglicht die Tatsache, den hayWert in der Zusicherung von anzugeben, Animal::getFavoriteFood()auch eine genauere Zusicherung der zurückgegebenen Animal. Wir möchten, dass die Objekte für einige Eigenschaften nicht gleich sind, aber nicht unbedingt für alle Eigenschaften.
Die Verwendung einer Matcher-API ist also definitiv klarer und flexibler.

davidxxx
quelle
-1

Die Modulo-API-Konsistenz, warum JUnit nicht assertNotEquals()bereitgestellt hat, ist der gleiche Grund, warum JUnit niemals Methoden wie bereitgestellt hat

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

Das heißt, es gibt kein Ende, bestimmte Assertionsmethoden für die Art von Dingen bereitzustellen, die Sie in Ihrer Assertionslogik wünschen könnten!

Weit besser zusammensetzbare Test Primitiven , wie zur Verfügung zu stellen equalTo(...), is(...), not(...), regex(...)und lassen Sie den Programmierer Stück diejenigen zusammen , anstatt für mehr Lesbarkeit und geistige Gesundheit.

Fatuhoku
quelle
3
Aus irgendeinem Grund existiert assertEquals (). Es musste nicht, aber es tut. Die Frage war nach dem Mangel an Symmetrie - warum gibt es assertEquals, aber nicht sein Gegenstück?
foo