Saya mencoba menulis pengujian unit untuk berbagai clone()
operasi di dalam proyek besar dan saya bertanya-tanya apakah ada kelas yang ada di suatu tempat yang mampu mengambil dua objek dengan tipe yang sama, melakukan perbandingan mendalam, dan mengatakan jika mereka identik atau tidak?
java
comparison
equals
Uri
sumber
sumber
Jawaban:
Unitils memiliki fungsi ini:
sumber
unitils
cacat justru karena membandingkan variabel bahkan ketika mereka mungkin tidak memiliki dampak yang dapat diamati . Konsekuensi lain (tidak diinginkan) dari membandingkan variabel adalah bahwa penutupan murni (tanpa statusnya sendiri) tidak didukung. Selain itu, ini membutuhkan objek yang dibandingkan dengan jenis runtime yang sama. Saya menyingsingkan lengan baju dan membuat alat perbandingan mendalam versi saya sendiri yang membahas masalah ini.Saya suka pertanyaan ini! Terutama karena hampir tidak pernah dijawab atau dijawab dengan buruk. Sepertinya belum ada yang tahu. Wilayah perawan :)
Pertama, jangan pernah berpikir untuk menggunakan
equals
. Kontrakequals
, sebagaimana didefinisikan dalam javadoc, adalah relasi ekivalen (refleksif, simetris, dan transitif), bukan relasi kesetaraan. Untuk itu, juga harus antisimetris. Satu-satunya implementasi dariequals
itu adalah (atau bisa jadi) hubungan kesetaraan yang sebenarnya adalah yang ada dijava.lang.Object
. Bahkan jika Anda menggunakannyaequals
untuk membandingkan semua yang ada di grafik, risiko melanggar kontrak cukup tinggi. Seperti yang ditunjukkan Josh Bloch dalam Effective Java , kontrak yang setara sangat mudah diputuskan:Selain itu, apa gunanya metode boolean bagi Anda? Akan menyenangkan untuk benar-benar merangkum semua perbedaan antara yang asli dan klon, bukan begitu? Selain itu, saya akan berasumsi di sini bahwa Anda tidak ingin direpotkan dengan menulis / memelihara kode perbandingan untuk setiap objek dalam grafik, tetapi Anda sedang mencari sesuatu yang akan diskalakan dengan sumber karena berubah seiring waktu.
Jadi, yang benar-benar Anda inginkan adalah semacam alat perbandingan keadaan. Bagaimana alat itu diterapkan sangat bergantung pada sifat model domain Anda dan batasan kinerja Anda. Menurut pengalaman saya, tidak ada peluru ajaib yang umum. Dan itu akan menjadi lambat pada sejumlah besar iterasi. Tetapi untuk menguji kelengkapan operasi klon, itu akan melakukan pekerjaan dengan cukup baik. Dua pilihan terbaik Anda adalah serialisasi dan refleksi.
Beberapa masalah yang akan Anda temui:
XStream cukup cepat dan dikombinasikan dengan XMLUnit akan melakukan pekerjaan hanya dalam beberapa baris kode. XMLUnit bagus karena dapat melaporkan semua perbedaan, atau berhenti pada yang pertama ditemukan. Dan hasilnya mencakup xpath ke node yang berbeda, yang bagus. Secara default tidak mengizinkan koleksi yang tidak berurutan, tetapi dapat dikonfigurasi untuk melakukannya. Dengan memasukkan penangan perbedaan khusus (Disebut a
DifferenceListener
), Anda dapat menentukan cara yang Anda inginkan untuk menangani perbedaan, termasuk mengabaikan urutan. Namun, segera setelah Anda ingin melakukan apa pun selain penyesuaian yang paling sederhana, penulisannya menjadi sulit dan detailnya cenderung terikat pada objek domain tertentu.Preferensi pribadi saya adalah menggunakan refleksi untuk menggilir semua bidang yang dinyatakan dan menelusuri ke masing-masing, melacak perbedaan saat saya pergi. Kata peringatan: Jangan gunakan rekursi kecuali Anda menyukai pengecualian stack overflow. Jaga hal-hal dalam ruang lingkup dengan tumpukan (gunakan file
LinkedList
atau sesuatu). Saya biasanya mengabaikan bidang sementara dan statis, dan saya melewatkan pasangan objek yang telah saya bandingkan, jadi saya tidak berakhir di loop tanpa batas jika seseorang memutuskan untuk menulis kode referensi sendiri (Namun, saya selalu membandingkan pembungkus primitif apa pun yang terjadi. , karena referensi objek yang sama sering digunakan kembali). Anda dapat mengonfigurasi hal-hal di depan untuk mengabaikan pengurutan koleksi dan untuk mengabaikan jenis atau bidang khusus, tetapi saya ingin menentukan kebijakan perbandingan status saya pada bidang itu sendiri melalui penjelasan. Ini, IMHO, adalah persis apa penjelasan yang dimaksudkan untuk, untuk membuat data meta tentang kelas tersedia pada waktu proses. Sesuatu seperti:Saya pikir ini sebenarnya masalah yang sangat sulit, tetapi benar-benar bisa dipecahkan! Dan begitu Anda memiliki sesuatu yang sesuai untuk Anda, itu sangat, sangat, berguna :)
Jadi semoga berhasil. Dan jika Anda menemukan sesuatu yang benar-benar jenius, jangan lupa untuk membagikannya!
sumber
Lihat DeepEquals dan DeepHashCode () dalam java-util: https://github.com/jdereg/java-util
Kelas ini melakukan apa yang diminta oleh penulis asli.
sumber
public
harus berlaku untuk semua jenis sebagai lawan hanya berlaku untuk koleksi / peta.Override The equals () Metode
Anda cukup mengganti metode equals () kelas menggunakan EqualsBuilder.reflectionEquals () seperti yang dijelaskan di sini :
sumber
Hanya harus menerapkan perbandingan dua contoh entitas yang direvisi oleh Hibernate Envers. Saya mulai menulis perbedaan saya sendiri tetapi kemudian menemukan kerangka kerja berikut.
https://github.com/SQiShER/java-object-diff
Anda dapat membandingkan dua objek dengan tipe yang sama dan itu akan menunjukkan perubahan, penambahan dan penghapusan. Jika tidak ada perubahan, maka objeknya sama (secara teori). Anotasi disediakan untuk pengambil yang harus diabaikan selama pemeriksaan. Kerangka kerja memiliki aplikasi yang jauh lebih luas daripada pemeriksaan kesetaraan, yaitu yang saya gunakan untuk menghasilkan log perubahan.
Kinerjanya OK, saat membandingkan entitas JPA, pastikan untuk melepaskannya dari pengelola entitas terlebih dahulu.
sumber
Saya menggunakan XStream:
sumber
Di AssertJ , Anda dapat melakukan:
Mungkin itu tidak akan berfungsi di semua kasus, namun akan berfungsi di lebih banyak kasus yang Anda pikirkan.
Inilah yang dikatakan dokumentasinya:
sumber
isEqualToComparingFieldByFieldRecursively
sekarang tidak digunakan lagi. GunakanassertThat(expectedObject).usingRecursiveComparison().isEqualTo(actualObject);
sebagai gantinya :)http://www.unitils.org/tutorial-reflectionassert.html
sumber
Hamcrest memiliki Matcher samePropertyValuesAs . Tapi itu bergantung pada Konvensi JavaBeans (menggunakan getter dan setter). Jika objek yang akan dibandingkan tidak memiliki getter dan setter untuk atributnya, ini tidak akan berfungsi.
Kacang pengguna - dengan getter dan setter
sumber
isFoo
metode baca untuk sebuahBoolean
properti. Ada PR yang dibuka sejak 2016 untuk memperbaikinya. github.com/hamcrest/JavaHamcrest/pull/136Jika objek Anda mengimplementasikan Serializable, Anda dapat menggunakan ini:
sumber
Contoh Daftar Tertaut Anda tidak terlalu sulit untuk ditangani. Saat kode melintasi dua grafik objek, ia menempatkan objek yang dikunjungi dalam Set atau Peta. Sebelum melintasi ke referensi objek lain, set ini diuji untuk melihat apakah objek telah dilintasi. Jika demikian, tidak perlu melangkah lebih jauh.
Saya setuju dengan orang di atas yang mengatakan menggunakan LinkedList (seperti Stack tetapi tanpa metode sinkronisasi di atasnya, jadi lebih cepat). Melintasi grafik objek menggunakan Tumpukan, sambil menggunakan refleksi untuk mendapatkan setiap bidang, adalah solusi yang ideal. Ditulis sekali, ini "eksternal" sama dengan () dan "eksternal" hashCode () adalah apa yang harus dipanggil oleh semua metode sama dengan () dan hashCode (). Anda tidak perlu lagi metode pelanggan sama dengan ().
Saya menulis sedikit kode yang melintasi grafik objek lengkap, terdaftar di Google Code. Lihat json-io (http://code.google.com/p/json-io/). Ini membuat serial grafik objek Java menjadi JSON dan deserialisasi darinya. Ini menangani semua objek Java, dengan atau tanpa konstruktor publik, Serializeable atau tidak Serializable, dll. Kode traversal yang sama ini akan menjadi dasar untuk implementasi eksternal "equals ()" dan eksternal "hashcode ()". Btw, JsonReader / JsonWriter (json-io) biasanya lebih cepat daripada ObjectInputStream / ObjectOutputStream bawaan.
JsonReader / JsonWriter ini dapat digunakan untuk perbandingan, tetapi tidak akan membantu dengan kode hash. Jika Anda menginginkan kode hash universal () dan sama dengan (), itu membutuhkan kode itu sendiri. Saya mungkin dapat melakukan ini dengan pengunjung grafik umum. Kita lihat saja nanti.
Pertimbangan lain - bidang statis - itu mudah - mereka dapat dilewati karena semua instance sama dengan () akan memiliki nilai yang sama untuk bidang statis, karena bidang statis dibagikan ke semua instance.
Adapun bidang transien - itu akan menjadi opsi yang dapat dipilih. Terkadang Anda mungkin ingin transien menghitung kali lain tidak. "Terkadang Anda merasa seperti orang gila, terkadang tidak."
Periksa kembali ke proyek json-io (untuk proyek saya yang lain) dan Anda akan menemukan proyek ekuivalen () / hashcode () eksternal. Saya belum punya nama untuk itu, tapi itu akan terlihat jelas.
sumber
Apache memberi Anda sesuatu, mengonversi kedua objek menjadi string dan membandingkan string, tetapi Anda harus Menimpa toString ()
Ganti toString ()
Jika semua bidang adalah tipe primitif:
Jika Anda memiliki bidang dan / atau koleksi dan / atau peta non primitif:
sumber
Saya rasa Anda tahu ini, tetapi secara teori, Anda seharusnya selalu menimpa .equals untuk menyatakan bahwa dua objek benar-benar sama. Ini akan menyiratkan bahwa mereka memeriksa metode .equals yang diganti pada anggotanya.
Hal semacam inilah yang menyebabkan .equals didefinisikan di Object.
Jika ini dilakukan secara konsisten, Anda tidak akan mendapat masalah.
sumber
Jaminan yang terhenti untuk perbandingan yang begitu dalam mungkin menjadi masalah. Apa yang harus dilakukan berikut ini? (Jika Anda menerapkan pembanding seperti itu, ini akan menjadi pengujian unit yang baik.)
Ini yang lainnya:
sumber
Using an answer instead of a comment to get a longer limit and better formatting.
Jika ini adalah komentar, mengapa menggunakan bagian jawaban? Itulah mengapa saya menandainya. bukan karena?
. Jawaban ini sudah ditandai oleh orang lain, yang tidak meninggalkan komentarnya. Saya baru saja mendapatkan ini di antrean tinjauan. Bisa jadi saya buruk itu, saya seharusnya lebih berhati-hati.Saya pikir solusi termudah yang terinspirasi oleh solusi Ray Hulha adalah dengan membuat serial objek dan kemudian membandingkan hasil mentahnya secara mendalam.
Serialisasi dapat berupa byte, json, xml atau toString sederhana, dll. ToString tampaknya lebih murah. Lombok menghasilkan ToSTring gratis yang dapat disesuaikan dan mudah untuk kami. Lihat contoh di bawah ini.
sumber
Sejak Java 7
Objects.deepEquals(Object, Object)
,.sumber