Mengapa JUnit tidak menyediakan metode assertNotEquals?

429

Adakah yang tahu mengapa JUnit 4 menyediakan assertEquals(foo,bar)tetapi tidak menggunakan assertNotEqual(foo,bar)metode?

Ini menyediakan assertNotSame(sesuai dengan assertSame) dan assertFalse(sesuai dengan assertTrue), sehingga tampak aneh bahwa mereka tidak repot termasuk assertNotEqual.

Omong-omong, saya tahu bahwa JUnit-addons menyediakan metode yang saya cari. Aku hanya bertanya karena penasaran.

Chris B
sumber
Setidaknya sejak JUnit 4.12, assertNotEquals disediakan. junit.org/junit4/javadoc/4.12/org/junit/…
WebF0x

Jawaban:

403

Saya sarankan Anda menggunakan pernyataan assertThat()gaya yang lebih baru , yang dapat dengan mudah menggambarkan semua jenis negasi dan secara otomatis membangun deskripsi tentang apa yang Anda harapkan dan apa yang Anda dapatkan jika pernyataan gagal:

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

Ketiga opsi itu setara, pilih yang menurut Anda paling mudah dibaca.

Untuk menggunakan nama-nama sederhana metode (dan memungkinkan sintaksis tegang ini bekerja), Anda perlu impor ini:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
Joachim Sauer
sumber
134
Saya menghargai pointer ke sintaks pernyataan alternatif, tetapi menunjuk ke tempat lain tidak menjawab mengapa JUnit tidak pernah menyediakannya assertNotEquals().
seh
14
@ Seh: Cara saya membacanya pertanyaannya bukan tentang kepentingan sejarah, tetapi tentang cara untuk merumuskan pernyataan "dua objek ini tidak sama" dalam tes JUnit. Saya jawab itu. Mempertimbangkan "mengapa tidak ada assertNotEqual" saya akan mengatakan itu karena itu adalah pernyataan khusus yang tidak diperlukan sesering assertEqualsdan karena itu akan diekspresikan melalui generik assertFalse.
Joachim Sauer
21
"pilih yang menurut Anda paling mudah dibaca". Orang yang membaca dan menulis tes unit adalah programmer. Apakah mereka benar-benar menemukan ini lebih mudah dibaca daripada assertNotEqual (objectUnderTest, someOtherObject) atau assertFalse (objectUnderTest.equals (someOtherObject))? Saya tidak yakin dengan API pencocokan mewah - tampaknya akan jauh lebih sulit bagi seorang programmer untuk mengeksplorasi / menemukan cara menggunakannya ...
bacar
@ Bacac: untuk beberapa menegaskan itu pada dasarnya masalah gaya. Tetapi assertThatjauh lebih ekspresif daripada set terbatas assert*metode yang tersedia. Oleh karena itu Anda dapat mengungkapkan kendala yang tepat dalam satu baris, membuatnya (hampir) membaca seperti kalimat bahasa Inggris dan mendapatkan pesan yang bermakna ketika pernyataan gagal. Memang, itu tidak selalu merupakan fitur pembunuh, tetapi ketika Anda telah melihatnya beraksi beberapa kali, Anda akan melihat berapa banyak nilai yang ditambahkan.
Joachim Sauer
5
@ Joachim Saya setuju itu assertThatlebih ekspresif daripada assert*, tapi saya tidak berpikir itu lebih ekspresif daripada ekspresi java yang dapat Anda masukkan ke dalam dan keluar dari assert*ekspresi secara umum (setelah semua saya dapat mengekspresikan apa pun dalam kode java). Ini adalah masalah umum yang saya mulai temukan dengan API gaya lancar - setiap orang pada dasarnya adalah DSL baru yang harus Anda pelajari (ketika kita semua sudah tahu yang Jawa!). Saya kira Hamcrest sudah ada di mana-mana sekarang karena masuk akal untuk mengharapkan orang mengetahuinya. Saya akan bermain ...
bacar
154

Ada assertNotEqualsdalam 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;
Stefan Birkner
sumber
1
Pikiran, salah satu artefak mock (2.6.0) bocor versi lama junit-dep yang pada gilirannya memiliki kelas Assert tanpa assertNotEquals. Lebih baik kecualikan itu saat menggunakan ivy.
gkephorus
7
Saya menggunakan 4.12 tetapi masih tidak dapat menemukan assertNotEqual. : s
Mubashar
49

Saya juga heran. API Assert tidak terlalu simetris; untuk menguji apakah objek sama, ia menyediakan assertSamedan assertNotSame.

Tentu saja, tidak terlalu lama untuk menulis:

assertFalse(foo.equals(bar));

Dengan pernyataan seperti itu, satu-satunya bagian informatif dari output sayangnya adalah nama metode pengujian, sehingga pesan deskriptif harus dibentuk secara terpisah:

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

Tentu saja itu sangat membosankan, lebih baik Anda menggulungnya sendiri assertNotEqual. Untungnya di masa depan mungkin akan menjadi bagian dari JUnit: JUnit edisi 22

Mikko Maunu
sumber
19
Tetapi ini kurang bermanfaat, karena JUnit tidak dapat menghasilkan pesan kegagalan yang membantu memberi tahu Anda, misalnya, nilai foo and bar yang tidak sama. Alasan kegagalan sebenarnya disembunyikan dan diubah menjadi boolean sederhana.
Ben James
3
Saya sangat setuju. Terutama assertFalse membutuhkan argumen pesan yang tepat untuk menghasilkan output untuk mengetahui apa yang sebenarnya salah.
Mikko Maunu
Saya pikir ini berguna untuk tes teks saat ini. Thnx
Marouane Gazanayi
Masalah dengan teks adalah bahwa itu akan ketinggalan zaman ketika kode berkembang.
Mark Levison
13

Saya berpendapat bahwa tidak adanya assertNotEqual memang asimetri dan membuat JUnit sedikit kurang bisa dipelajari. Pikiran bahwa ini adalah kasus yang rapi ketika menambahkan metode akan mengurangi kompleksitas API, setidaknya bagi saya: Simetri membantu mengatur ruang yang lebih besar. Dugaan saya adalah bahwa alasan penghilangan tersebut mungkin karena terlalu sedikit orang yang memanggil metode ini. Namun, saya ingat saat ketika bahkan menyatakan Salah tidak ada; karenanya, saya memiliki harapan positif bahwa metode ini pada akhirnya mungkin ditambahkan, mengingat bahwa itu bukan metode yang sulit; meskipun saya mengakui bahwa ada banyak solusi, bahkan yang elegan.

Bernhard Bodenstorfer
sumber
7

Saya datang ke pesta ini agak terlambat tetapi saya menemukan bahwa formulir:

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

dapat dibuat berfungsi untuk sebagian besar kasus 'tidak sama'.

int status = doSomething() ; // expected to return 123
assertTrue("doSomething() returned unexpected status", status != 123 ) ;
user903724
sumber
4
Meskipun ini berhasil, masalahnya adalah jika pernyataan itu gagal, itu hanya akan mengatakan "Dipuji benar, tetapi salah", atau pernyataan tidak jelas lainnya. Apa yang akan hebat adalah jika itu Diharapkan Bukan 123, tetapi 123.
Stealth Rabbi
6

Saya sedang mengerjakan JUnit di lingkungan java 8, menggunakan jUnit4.12

bagi saya: kompiler tidak dapat menemukan metode assertNotEquals, bahkan ketika saya menggunakannya
import org.junit.Assert;

Jadi saya berubah
assertNotEquals("addb", string);
menjadi
Assert.assertNotEquals("addb", string);

Jadi, jika Anda menghadapi masalah tentang assertNotEqualtidak dikenal, maka ubahlah untuk Assert.assertNotEquals(,);itu harus menyelesaikan masalah Anda

Akshay Vijay Jain
sumber
1
Itu karena metodenya statis, dan Anda harus mengimpornya secara statis. Gunakan ini import static org.junit.Assert.*;dan Anda akan dapat menggunakan semua pernyataan tanpa nama kelas.
Tom Stone
3

Alasan yang jelas bahwa orang menginginkan assertNotEquals () adalah untuk membandingkan builtin tanpa harus mengonversinya menjadi objek yang penuh sesak nafas terlebih dahulu:

Contoh lisan:

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

vs.

assertNotEqual(1, winningBidderId);

Sayangnya sejak Eclipse tidak memasukkan JUnit 4.11 secara default, Anda harus verbose.

Peringatan Saya tidak berpikir '1' perlu dibungkus dengan Integer.valueOf () tapi karena saya baru kembali dari .NET tidak mengandalkan kebenaran saya.

Mark Levison
sumber
1

Lebih baik menggunakan Hamcrest untuk pernyataan negatif daripada pernyataan palsu. Seperti pada laporan tes sebelumnya akan menunjukkan perbedaan untuk kegagalan pernyataan.

Jika Anda menggunakan assertFalse, Anda hanya mendapatkan kegagalan pernyataan dalam laporan. yaitu kehilangan informasi tentang penyebab kegagalan.

Chris Kelly
sumber
1

Biasanya saya melakukan ini ketika saya berharap dua objek sama:

assertTrue(obj1.equals(obj2));

dan:

assertFalse(obj1.equals(obj2));

ketika mereka diharapkan tidak setara. Saya sadar bahwa ini bukan jawaban untuk pertanyaan Anda tetapi ini adalah yang terdekat yang bisa saya dapatkan. Ini dapat membantu orang lain mencari apa yang dapat mereka lakukan dalam versi JUnit sebelum JUnit 4.11.

Willempie
sumber
0

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 Animalparameter 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 Animalnegara::

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::getFavoriteFoodnilai - 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: namedan agetidak dinilai dengan benar. Selain itu, fakta menentukan haynilai 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.

davidxxx
sumber
-1

Modulo API konsistensi, mengapa JUnit tidak memberikan assertNotEquals()alasan yang sama mengapa JUnit tidak pernah memberikan metode seperti

  • assertStringMatchesTheRegex(regex, str) vs. assertStringDoesntMatchTheRegex(regex, str)
  • assertStringBeginsWith(prefix, str) vs. assertStringDoesntBeginWith(prefix, str)

yaitu tidak ada akhir untuk memberikan metode pernyataan spesifik untuk hal-hal yang mungkin Anda inginkan dalam logika pernyataan Anda!

Jauh lebih baik untuk memberikan primitif tes composable seperti equalTo(...), is(...), not(...), regex(...)dan membiarkan programmer potongan mereka bersama-sama, bukan untuk lebih mudah dibaca dan kewarasan.

fatuhoku
sumber
3
baik, untuk beberapa alasan, assertEquals () ada. Tidak harus, tetapi itu harus dilakukan. Pertanyaannya adalah tentang kurangnya simetri - mengapa asertEquals ada tetapi tidak ada padanannya?
foo