เหตุใด JUnit จึงไม่ให้วิธีการ assertNotEquals


429

มีใครรู้บ้างไหมว่าทำไม JUnit 4 จึงมีวิธีการassertEquals(foo,bar)แต่ไม่ใช่assertNotEqual(foo,bar)

มันมีassertNotSame(ตรงกับassertSame) และassertFalse(ตรงกับassertTrue) assertNotEqualดังนั้นจึงดูเหมือนว่าแปลกที่พวกเขาไม่ได้รำคาญรวมถึง

โดยวิธีการที่ฉันรู้ว่า JUnit-addons ให้วิธีการที่ฉันกำลังมองหา ฉันแค่ขอจากความอยากรู้


อย่างน้อยตั้งแต่ JUnit 4.12 มีการระบุ assertNotEquals junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

คำตอบ:


403

ฉันขอแนะนำให้คุณใช้การassertThat()ยืนยันแบบใหม่ซึ่งสามารถอธิบายการปฏิเสธทุกประเภทได้อย่างง่ายดายและสร้างคำอธิบายโดยอัตโนมัติตามที่คุณคาดหวังและสิ่งที่คุณได้รับหากการยืนยันล้มเหลว:

assertThat(objectUnderTest, is(not(someOtherObject)));
assertThat(objectUnderTest, not(someOtherObject));
assertThat(objectUnderTest, not(equalTo(someOtherObject)));

ทั้งสามตัวเลือกนั้นเทียบเท่ากันเลือกตัวเลือกที่คุณสามารถอ่านได้มากที่สุด

ในการใช้ชื่อแบบเรียบง่ายของเมธอด (และอนุญาตให้ใช้งานไวยากรณ์ที่ตึงเครียด) คุณต้องมีการนำเข้าเหล่านี้:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

134
ฉันขอขอบคุณที่ชี้ไปยังไวยากรณ์ยืนยันสำรอง แต่ชี้ไปที่อื่นไม่ได้คำตอบว่าทำไม JUnit assertNotEquals()ไม่เคยให้ไว้
SEH

14
@ seh: วิธีที่ฉันอ่านคำถามไม่เกี่ยวกับความสนใจทางประวัติศาสตร์ แต่เกี่ยวกับวิธีการยืนยัน "วัตถุทั้งสองนี้ไม่เท่ากัน" ในการทดสอบ JUnit ฉันตอบว่า พิจารณา "ทำไม / เป็นไม่มีassertNotEqual" ผมบอกว่าเพราะมันเป็นยืนยันพิเศษที่ไม่จำเป็นบ่อยเท่าจึงจะแสดงผ่านทางทั่วไปassertEquals assertFalse
Joachim Sauer

21
"เลือกรายการที่คุณอ่านได้มากที่สุด" ผู้ที่อ่านและเขียนการทดสอบหน่วยเป็นโปรแกรมเมอร์ พวกเขาพบว่าสิ่งนี้สามารถอ่านได้มากกว่า assertNotEqual (objectUnderTest, someOtherObject) หรือ assertFalse (objectUnderTest.equals (someOtherObject)) หรือไม่ ฉันไม่มั่นใจกับ API ตัวจับคู่แฟนซี - ดูเหมือนว่าจะยากกว่าสำหรับโปรแกรมเมอร์ที่จะสำรวจ / ค้นพบวิธีใช้งาน ...
bacar

@ บาคาร์: สำหรับบางคนอ้างว่ามันเป็นเรื่องของสไตล์ แต่assertThatเป็นการแสดงออกที่ชัดเจนกว่าชุดที่ จำกัดassert*วิธีการที่ใช้ได้ ดังนั้นคุณสามารถแสดงข้อ จำกัด ที่แน่นอนในบรรทัดเดียวให้มัน (เกือบ) อ่านเหมือนประโยคภาษาอังกฤษ และได้รับข้อความที่มีความหมายเมื่อยืนยันล้มเหลว ได้รับนั่นไม่ใช่คุณสมบัตินักฆ่าเสมอไป แต่เมื่อคุณได้เห็นมันในการดำเนินการสองสามครั้งคุณจะเห็นว่ามันเพิ่มคุณค่ามากเพียงใด
โจอาคิมซาวเออร์

5
@ โจอาคิมฉันเห็นด้วยว่าassertThatมันมีความหมายมากกว่าassert*แต่ฉันไม่คิดว่ามันจะแสดงออกได้ดีไปกว่านิพจน์ของจาวาที่คุณสามารถใส่ทั้งด้านในและด้านนอกของassert*นิพจน์ทั่วไปได้ มันเป็นปัญหาทั่วไปที่ฉันเริ่มพบกับ API ที่ใช้ภาษาได้อย่างคล่องแคล่ว - ทุกคนนั้นเป็น DSL ใหม่ที่คุณต้องเรียนรู้ (เมื่อเรารู้จัก Java แล้ว!) ฉันคิดว่า Hamcrest แพร่หลายมากพอที่จะให้ผู้คนรู้ได้ ฉันจะเล่น ...
บาคาร่า

154

มีอยู่assertNotEqualsใน JUnit 4.11: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.11.md#improvements-to-assert-and-assume

import static org.junit.Assert.assertNotEquals;

1
ใจหนึ่งใน jmock (2.6.0) สิ่งประดิษฐ์ maven รั่วไหลรุ่นเก่าของ junit-dep ซึ่งจะมีการยืนยันระดับโดยไม่ต้อง assertNotEquals ดีกว่ายกเว้นว่าเมื่อใช้ไม้เลื้อย
gkephorus

7
ฉันใช้ 4.12 แต่ก็ยังหา assertNotEqual ไม่ได้ : s
Mubashar

49

ฉันสงสัยเหมือนกัน API ของ Assert นั้นไม่สมมาตรมาก สำหรับการทดสอบว่ามีวัตถุเหมือนกันหรือไม่assertSameassertNotSameและ

แน่นอนว่าไม่นานนักที่จะเขียน:

assertFalse(foo.equals(bar));

ด้วยการยืนยันดังกล่าวส่วนที่ให้ข้อมูลเพียงอย่างเดียวของผลลัพธ์นั้นน่าเสียดายที่ชื่อของวิธีการทดสอบดังนั้นข้อความอธิบายจึงควรเกิดขึ้นแยกต่างหาก:

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

assertNotEqualนั่นคือของหลักสูตรที่น่าเบื่อเพื่อที่มันจะดีกว่าที่จะม้วนของคุณเอง โชคดีในอนาคตมันอาจจะเป็นส่วนหนึ่งของ JUnit: JUnit ฉบับที่ 22


19
แต่สิ่งนี้มีประโยชน์น้อยกว่าเพราะ JUnit ไม่สามารถสร้างข้อความแจ้งความล้มเหลวที่เป็นประโยชน์ที่จะบอกคุณตัวอย่างเช่นค่า foo และ bar ที่ไม่เท่ากัน เหตุผลความล้มเหลวที่แท้จริงถูกซ่อนไว้และเปลี่ยนเป็นบูลีนแบบง่าย
เบ็นเจมส์

3
ฉันเห็นด้วยอย่างยิ่ง โดยเฉพาะอย่างยิ่งยืนยันว่าต้องมีการโต้แย้งข้อความที่เหมาะสมในการผลิตผลลัพธ์ที่จะบอกสิ่งที่ผิดพลาดจริง ๆ
Mikko Maunu

ฉันคิดว่านี่เป็นประโยชน์สำหรับการทดสอบข้อความปัจจุบัน ขอบคุณ
มารูยานเฟล Gazanayi

ปัญหาเกี่ยวกับข้อความคือมันจะล้าสมัยเนื่องจากโค้ดวิวัฒนาการ
Mark Levison

13

ฉันยืนยันว่าการขาด assertNotEqual นั้นไม่สมมาตรและทำให้ JUnit เรียนรู้ได้น้อยลง โปรดทราบว่านี่เป็นกรณีที่เรียบร้อยเมื่อมีการเพิ่มวิธีการจะช่วยลดความซับซ้อนของ API อย่างน้อยสำหรับฉัน: Symmetry ช่วยให้มีพื้นที่มากขึ้น ฉันเดาว่าเหตุผลของการละเว้นอาจเป็นเพราะมีคนน้อยเกินไปที่เรียกใช้วิธีนี้ กระนั้นฉันก็ยังจำช่วงเวลาที่ถึงแม้จะยืนยันว่าปลอมไม่ได้อยู่ ดังนั้นฉันมีความคาดหวังในเชิงบวกว่าในที่สุดวิธีการอาจเพิ่มเข้ามาเนื่องจากมันไม่ใช่วิธีที่ยาก แม้ว่าฉันจะยอมรับว่ามีวิธีแก้ไขปัญหามากมาย แต่ก็ยังดีเยี่ยม


7

ฉันมางานปาร์ตี้นี้ค่อนข้างช้า แต่ฉันพบว่าแบบฟอร์มนี้:

static void assertTrue(java.lang.String message, boolean condition) 

สามารถทำให้การทำงานสำหรับกรณีส่วนใหญ่ 'ไม่เท่ากับ'

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;

4
ในขณะที่ใช้งานได้ปัญหาคือถ้าการยืนยันล้มเหลวมันก็จะพูดว่า "exepcted จริง แต่เป็นเท็จ" หรือคำสั่งที่ไม่ชัดเจนอื่น ๆ สิ่งที่จะดีคือถ้ามันเป็นที่คาดหวังไม่ใช่ 123 แต่เป็น 123
Stealth Rabbi

6

ฉันทำงานกับ JUnit ในสภาพแวดล้อม java 8 โดยใช้ jUnit4.12

สำหรับฉัน: คอมไพเลอร์ไม่สามารถหาวิธีการ assertNotEquals ได้แม้เมื่อฉันใช้
import org.junit.Assert;

ดังนั้นฉันเปลี่ยน
assertNotEquals("addb", string);
เป็น
Assert.assertNotEquals("addb", string);

ดังนั้นหากคุณกำลังประสบปัญหาเกี่ยวกับการassertNotEqualไม่รู้จักให้เปลี่ยนAssert.assertNotEquals(,);เป็นควรแก้ไขปัญหาของคุณ


1
นั่นเป็นเพราะวิธีการแบบคงที่และคุณต้องนำเข้าแบบคงที่ ใช้สิ่งนี้import static org.junit.Assert.*;และคุณจะสามารถใช้การยืนยันทั้งหมดโดยไม่มีชื่อคลาส
Tom Stone

3

เหตุผลที่ชัดเจนว่าผู้คนต้องการ assertNotEquals () คือการเปรียบเทียบ builtins โดยไม่ต้องแปลงเป็นวัตถุเป่าเต็มก่อน:

ตัวอย่างที่ชัดเจน:

....
assertThat(1, not(equalTo(Integer.valueOf(winningBidderId))));
....

เมื่อเทียบกับ

assertNotEqual(1, winningBidderId);

น่าเศร้าเนื่องจาก Eclipse ไม่ได้รวม JUnit 4.11 ตามค่าเริ่มต้นคุณต้องเป็น verbose

ข้อแม้ฉันไม่คิดว่า '1' จะต้องถูกรวมอยู่ใน Integer.valueOf () แต่เนื่องจากฉันเพิ่งกลับมาจาก. NET จะไม่นับความถูกต้องของฉัน


1

มันจะดีกว่าที่จะใช้ Hamcrest สำหรับการยืนยันเชิงลบมากกว่าที่จะยืนยันว่าปลอมเหมือนในอดีตรายงานการทดสอบจะแสดงความแตกต่างสำหรับความล้มเหลวในการยืนยัน

หากคุณใช้ assertFalse คุณจะได้รับความล้มเหลวในการยืนยันในรายงาน ข้อมูลที่หายไปคือสาเหตุของความล้มเหลว


1

ฉันมักจะทำสิ่งนี้เมื่อฉันคาดหวังว่าวัตถุสองรายการจะเท่ากัน:

assertTrue(obj1.equals(obj2));

และ:

assertFalse(obj1.equals(obj2));

เมื่อพวกเขาคาดว่าจะไม่เท่ากัน ฉันรู้ว่านี่ไม่ใช่คำตอบสำหรับคำถามของคุณ แต่มันใกล้เคียงที่สุดที่ฉันจะได้รับ มันสามารถช่วยให้ผู้อื่นค้นหาสิ่งที่พวกเขาสามารถทำได้ในรุ่น JUnit ก่อน JUnit 4.11


0

ฉันเห็นด้วยกับมุมมอง 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 นั้นชัดเจนและยืดหยุ่นกว่ามาก


-1

ความสอดคล้องของ Modulo API สาเหตุที่ JUnit ไม่ได้จัดเตรียมไว้assertNotEquals()เป็นเหตุผลเดียวกันกับที่ JUnit ไม่เคยให้วิธีการเช่นนั้น

  • assertStringMatchesTheRegex(regex, str) เมื่อเทียบกับ assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) เมื่อเทียบกับ assertStringDoesntBeginWith(prefix, str)

เช่นไม่มีที่สิ้นสุดที่จะให้วิธีการยืนยันที่เฉพาะเจาะจงสำหรับประเภทของสิ่งที่คุณอาจต้องการในตรรกะการยืนยันของคุณ!

ไกลกว่าที่จะให้พื้นฐานการทดสอบ composable เช่นequalTo(...), is(...), not(...), regex(...)และปล่อยให้ชิ้นโปรแกรมเมอร์ผู้แทนร่วมกันสำหรับการอ่านมากขึ้นและมีสุขภาพจิตดี


3
ดีด้วยเหตุผลบางอย่าง assertEquals () มีอยู่ มันไม่จำเป็นต้องทำ แต่ก็เป็นเช่นนั้น คำถามคือเกี่ยวกับการขาดความสมมาตร - ทำไม assertEquals มีอยู่ แต่ไม่ใช่ของคู่กัน?
foo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.