Ich stimme mit dem Standpunkt des Auftraggebers vollkommen überein. Assert.assertFalse(expected.equals(actual))
ist keine natürliche Art, eine Ungleichheit auszudrücken.
Ich würde jedoch behaupten, dass mehr als Assert.assertEquals()
, Assert.assertNotEquals()
funktioniert, aber es ist nicht benutzerfreundlich zu dokumentieren, was der Test tatsächlich behauptet und zu verstehen/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 benutzen.
Alternativ dazu verwende ich für die Behauptung des Zustands eines Objekts in der Regel eine Matcher-API, die den Zustand des Objekts leicht ermitteln kann, die die Absicht der Behauptungen klar dokumentiert und die sehr benutzerfreundlich ist, um die Ursache für das Scheitern einer Behauptung zu verstehen.
Hier ist ein Beispiel.
Angenommen, ich habe eine Tierklasse, die ich testen möchte die createWithNewNameAndAge()
Methode, eine Methode, die ein neues Tierobjekt erstellt, indem sie seinen Namen und sein Alter ändert, aber sein Lieblingsfutter beibehält.
Angenommen, ich verwende Assert.assertNotEquals()
zu behaupten, dass das ursprüngliche und das neue Objekt unterschiedlich sind.
Hier ist die Klasse Animal 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 erwartungsgemäß fehl, aber die dem Entwickler mitgeteilte Ursache ist wirklich nicht hilfreich. Er sagt nur, dass die Werte unterschiedlich sein sollten und gibt die toString()
Ergebnis, das auf dem aktuellen Animal
Parameter :
java.lang.AssertionError: Werte sollten unterschiedlich sein. Tatsächlich: Tier
[name=scoubi, age=10, favoriteFood=heu]
at org.junit.Assert.fail(Assert.java:88)
Ok, die Objekte sind nicht gleichwertig. Aber wo ist das Problem?
Welches Feld wird bei der getesteten Methode nicht korrekt bewertet? Eins ? Zwei ? Alle ?
Um sie zu entdecken, muss man in der Erde graben createWithNewNameAndAge()
Implementierung/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, was erhalten wird, machen würde.
JUnit 4.11 als Test-Runner und eine Test Matcher API als Assertion Tool
Hier das gleiche Szenario eines Tests, bei dem jedoch AssertJ (eine hervorragende Test-Matching-API) verwendet wird, um die Behauptung der Animal
Zustand: :
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 dieses Mal wird der Grund klar angegeben:
java.lang.AssertionError:
Erwarten:
<["scoubi", 10, "Heu"]>
genau (und in der gleichen Reihenfolge) zu enthalten:
<["kleiner Scoubi", 1, "Heu"]>
aber einige Elemente wurden nicht gefunden:
<["kleiner Scoubi", 1]>
und andere wurden nicht erwartet:
<["scoubi", 10]>
at junit5.MyTest.assertListNotEquals_AssertJ(MyTest.java:26)
Wir können das lesen für Animal::getName, Animal::getAge, Animal::getFavoriteFood
Werte des zurückgegebenen Tieres, so erwarten wir diese Werte:
"little scoubi", 1, "hay"
aber wir haben diese Werte gehabt:
"scoubi", 10, "hay"
Wir wissen also, wo die Ermittlungen stattfinden: name
y age
nicht korrekt bewertet werden. Außerdem ist die Tatsache, dass die Angabe der hay
Wert in der Behauptung von Animal::getFavoriteFood()
erlaubt es auch, die zurückgegebenen Daten feiner Animal
. Wir wollen, dass die Objekte für einige Eigenschaften nicht gleich sind, aber nicht unbedingt für alle Eigenschaften.
Die Verwendung einer Matcher-API ist also auf jeden Fall viel klarer und flexibler.