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 Animal
Parameter 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 Animal
Status 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::getFavoriteFood
wir 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: name
und age
werden nicht richtig bewertet. Darüber hinaus ermöglicht die Tatsache, den hay
Wert 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.