ฉันเห็นด้วยกับมุมมอง OP ทั้งหมด Assert.assertFalse(expected.equals(actual))
ไม่ใช่วิธีธรรมชาติในการแสดงความไม่เท่าเทียมกัน
แต่ฉันจะยืนยันว่ายิ่งกว่าAssert.assertEquals()
นั้น Assert.assertNotEquals()
ทำงานได้ แต่ไม่เป็นมิตรต่อผู้ใช้ในการบันทึกสิ่งที่การทดสอบจริงยืนยันและทำความเข้าใจ / แก้ไขข้อบกพร่องเมื่อการยืนยันล้มเหลว
ดังนั้นใช่ JUnit 4.11 และ JUnit 5 มีAssert.assertNotEquals()
(Assertions.assertNotEquals()
ใน JUnit 5) แต่ฉันหลีกเลี่ยงการใช้มันจริงๆ
เป็นทางเลือกเพื่อยืนยันสถานะของวัตถุที่ฉันใช้ทั่วไป matcher API ที่ขุดเข้าไปในสถานะวัตถุได้อย่างง่ายดายเอกสารที่ชัดเจนความตั้งใจของการยืนยันและที่เป็นมิตรกับผู้ใช้มากที่จะเข้าใจสาเหตุของความล้มเหลวในการยืนยัน
นี่คือตัวอย่าง
สมมติว่าฉันมีคลาสสัตว์ที่ฉันต้องการทดสอบcreateWithNewNameAndAge()
วิธีการวิธีการที่สร้างวัตถุสัตว์ใหม่โดยการเปลี่ยนชื่อและอายุของมัน แต่โดยการรักษาอาหารที่ชื่นชอบ
สมมติว่าฉันใช้ Assert.assertNotEquals()
เพื่อยืนยันว่าต้นฉบับและวัตถุใหม่แตกต่างกัน
นี่คือคลาสสัตว์ที่มีการใช้งานที่ไม่สมบูรณ์ของ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+ (หรือ JUnit 5) ทั้งสองเป็นเครื่องมือทดสอบและยืนยันการใช้งาน
@Test
void assertListNotEquals_JUnit_way() {
Animal scoubi = new Animal("scoubi", 10, "hay");
Animal littleScoubi = scoubi.createWithNewNameAndAge("little scoubi", 1);
Assert.assertNotEquals(scoubi, littleScoubi);
}
การทดสอบล้มเหลวตามที่คาดไว้ แต่สาเหตุที่ให้ไว้กับผู้พัฒนาไม่เป็นประโยชน์จริงๆ มันเพิ่งบอกว่าค่าควรแตกต่างกันและเอาท์พุทผลลัพธ์ที่toString()
เรียกใช้กับAnimal
พารามิเตอร์จริง:
java.lang.AssertionError: ค่าควรแตกต่างกัน จริง: สัตว์
[name = scoubi, อายุ = 10, favourite food = hay]
ที่ org.junit.Assert.fail (Assert.java:88)
ตกลงวัตถุไม่เท่ากับ แต่ปัญหาอยู่ที่ไหน
ฟิลด์ใดไม่ถูกต้องมูลค่าในวิธีการทดสอบ? หนึ่ง สอง ทั้งหมดนั้น ?
หากต้องการค้นพบมันคุณจะต้องขุดในcreateWithNewNameAndAge()
การนำไปใช้งาน / ใช้ดีบักเกอร์ในขณะที่การทดสอบ API จะเป็นมิตรมากขึ้นถ้ามันจะทำให้เรามีความแตกต่างระหว่างสิ่งที่คาดหวังและสิ่งที่ได้รับ
JUnit 4.11 เป็นผู้ทดสอบการทดสอบและ Matcher API เป็นเครื่องมือยืนยัน
ต่อไปนี้เป็นสถานการณ์จำลองการทดสอบเดียวกัน แต่ใช้ AssertJ (API การทดสอบ matcher ที่ยอดเยี่ยม) เพื่อยืนยันAnimal
สถานะ:
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");
}
แน่นอนว่าการทดสอบยังคงล้มเหลว แต่คราวนี้มีการระบุเหตุผลอย่างชัดเจน:
java.lang.AssertionError:
คาดหวังว่า:
<["scoubi", 10, "hay"]>
ที่จะมีอย่างแน่นอน (และในลำดับเดียวกัน):
<["little scoubi", 1, "hay"]>
แต่ไม่พบองค์ประกอบบางอย่าง:
<["little scoubi", 1]>
และคนอื่น ๆ ไม่คาดหวัง:
<["scoubi", 10]>
ที่ junit5.MyTest.assertListNotEquals_AssertJ (MyTest.java:26)
เราสามารถอ่านAnimal::getName, Animal::getAge, Animal::getFavoriteFood
ค่าของสัตว์ที่คืนมาได้เราคาดหวังว่าจะมีค่าเหล่านี้:
"little scoubi", 1, "hay"
แต่เรามีค่าเหล่านี้:
"scoubi", 10, "hay"
ดังนั้นเราจึงทราบว่าการตรวจสอบอยู่ที่ไหนname
และage
ไม่ได้รับคุณค่าอย่างถูกต้อง นอกจากนี้ความเป็นจริงของการระบุที่hay
คุ้มค่าในการยืนยันของช่วยให้ยังให้มากขึ้นอย่างประณีตยืนยันกลับAnimal::getFavoriteFood()
Animal
เราต้องการให้วัตถุนั้นไม่เหมือนกันสำหรับคุณสมบัติบางอย่าง แต่ไม่จำเป็นสำหรับทุกคุณสมบัติ
ดังนั้นการใช้ matcher API นั้นชัดเจนและยืดหยุ่นกว่ามาก