Saya setuju sepenuhnya dengan sudut pandang OP. Assert.assertFalse(expected.equals(actual))
bukanlah cara alami untuk mengekspresikan ketidaksetaraan.
Tapi saya berpendapat bahwa lebih jauh dari itu Assert.assertEquals()
, Assert.assertNotEquals()
bekerja tetapi tidak ramah pengguna untuk mendokumentasikan apa yang sebenarnya ditegaskan oleh tes dan untuk memahami / debug ketika pernyataan gagal.
Jadi ya JUnit 4.11 dan JUnit 5 menyediakan Assert.assertNotEquals()
(Assertions.assertNotEquals()
dalam JUnit 5) tapi saya benar-benar menghindari menggunakannya.
Sebagai alternatif, untuk menegaskan keadaan suatu objek, saya umumnya menggunakan pencocokan API yang menggali ke dalam keadaan objek dengan mudah, yang mendokumentasikan dengan jelas maksud pernyataan tersebut dan yang sangat ramah pengguna untuk memahami penyebab kegagalan pernyataan tersebut.
Berikut ini sebuah contoh.
Misalkan saya memiliki kelas Hewan yang ingin saya uji createWithNewNameAndAge()
metodenya, metode yang menciptakan objek Hewan baru dengan mengubah nama dan usianya tetapi dengan menyimpan makanan favoritnya.
Misalkan saya gunakan Assert.assertNotEquals()
untuk menyatakan bahwa objek asli dan baru berbeda.
Berikut adalah kelas Hewan dengan implementasi cacat 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+ (atau JUnit 5) baik sebagai test runner dan alat penegasan
@Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
Tes gagal seperti yang diharapkan tetapi penyebab yang diberikan kepada pengembang benar-benar tidak membantu. Ia hanya mengatakan bahwa nilainya harus berbeda dan mengeluarkan toString()
hasil yang dipanggil pada Animal
parameter aktual :
java.lang.AssertionError: Nilai harus berbeda. Aktual: Hewan
[nama = scoubi, usia = 10, favoriteFood = hay]
di org.junit.Assert.fail (Assert.java:88)
Ok objek tidak sama. Tapi di mana masalahnya?
Bidang mana yang tidak dinilai dengan benar dalam metode yang diuji? Satu ? Dua? Mereka semua ?
Untuk mengetahuinya, Anda harus menggali dalam createWithNewNameAndAge()
implementasi / menggunakan debugger sementara pengujian API akan jauh lebih ramah jika itu akan membuat kita perbedaan antara yang diharapkan dan yang didapat.
JUnit 4.11 sebagai pelari uji dan API Matcher uji sebagai alat penegasan
Di sini skenario pengujian yang sama tetapi yang menggunakan AssertJ (API pencocokan uji yang sangat baik) untuk membuat pernyataan Animal
negara::
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");
}
Tentu saja tes masih gagal tetapi kali ini alasannya dinyatakan dengan jelas:
java.lang.AssertionError:
Mengharapkan:
<["scoubi", 10, "hay"]>
mengandung persis (dan dalam urutan yang sama):
<["scoubi kecil", 1, "hay"]>
tetapi beberapa elemen tidak ditemukan:
<["scoubi kecil", 1]>
dan yang lainnya tidak diharapkan:
<["scoubi", 10]>
di junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)
Kita dapat membaca bahwa untuk Animal::getName, Animal::getAge, Animal::getFavoriteFood
nilai - nilai Hewan yang dikembalikan, kami berharap memiliki nilai ini:
"little scoubi", 1, "hay"
tetapi kami telah memiliki nilai-nilai ini:
"scoubi", 10, "hay"
Jadi kita tahu di mana menginvestigasi: name
dan age
tidak dinilai dengan benar. Selain itu, fakta menentukan hay
nilai dalam pernyataan Animal::getFavoriteFood()
memungkinkan juga untuk lebih halus menegaskan kembali Animal
. Kami ingin agar objek tidak sama untuk beberapa properti tetapi tidak harus untuk setiap properti.
Jadi pastinya, menggunakan matcher API jauh lebih jelas dan fleksibel.