Moderator Catatan: Sudah ada 39 jawaban yang diposting di sini (beberapa telah dihapus). Sebelum Anda memposting jawaban Anda , pertimbangkan apakah Anda dapat menambahkan sesuatu yang bermakna atau tidak untuk diskusi. Kemungkinan besar Anda hanya mengulangi apa yang sudah dikatakan orang lain.
Saya kadang-kadang menemukan diri saya perlu membuat metode pribadi di depan umum hanya untuk menulis beberapa tes unit untuk itu.
Biasanya ini terjadi karena metode ini berisi logika yang dibagi antara metode lain di kelas dan itu lebih rapi untuk menguji logika sendiri, atau alasan lain dapat dimungkinkan karena saya ingin menguji logika yang digunakan dalam utas sinkron tanpa harus khawatir tentang masalah threading .
Apakah orang lain mendapati diri mereka melakukan ini, karena saya tidak begitu suka melakukannya ?? Saya pribadi berpikir bonus lebih besar daripada masalah membuat metode publik yang tidak benar-benar menyediakan layanan di luar kelas ...
MEMPERBARUI
Terima kasih atas jawaban semua orang, tampaknya telah menarik minat masyarakat. Saya pikir konsensus umum pengujian harus terjadi melalui API publik karena ini adalah satu-satunya cara kelas akan pernah digunakan, dan saya setuju dengan ini. Beberapa kasus yang saya sebutkan di atas di mana saya akan melakukan ini di atas adalah kasus yang tidak biasa dan saya pikir manfaat melakukan itu sepadan.
Namun saya bisa, melihat setiap titik bahwa itu seharusnya tidak pernah benar-benar terjadi. Dan ketika memikirkannya sedikit lagi saya pikir mengubah kode Anda untuk mengakomodasi tes adalah ide yang buruk - setelah semua saya kira pengujian adalah alat pendukung dengan cara dan mengubah sistem untuk 'mendukung alat pendukung' jika Anda mau, adalah terang-terangan latihan yang buruk.
sumber
@VisibileForTesting
anotasi untuk membuat metode seperti itu - saya sarankan Anda melakukan ini sehingga alasan metode ini tidakprivate
didokumentasikan dengan baik.Jawaban:
Sebagai pernyataan umum, saya biasanya menggunakan kode "produksi" refactoring untuk membuatnya lebih mudah untuk diuji. Namun, saya tidak berpikir itu akan menjadi panggilan yang bagus di sini. Tes unit yang baik (biasanya) seharusnya tidak peduli dengan detail implementasi kelas, hanya tentang perilaku yang terlihat. Alih-alih mengekspos tumpukan internal untuk tes, Anda dapat menguji bahwa kelas mengembalikan halaman dalam urutan yang Anda harapkan setelah menelepon
first()
ataulast()
.Sebagai contoh, pertimbangkan kode-pseudo ini:
sumber
Secara pribadi, saya lebih suka unit test menggunakan API publik dan saya pasti tidak pernah membuat metode pribadi publik hanya untuk membuatnya mudah untuk diuji.
Jika Anda benar-benar ingin menguji metode pribadi secara terpisah, di Jawa Anda bisa menggunakan Easymock / Powermock untuk memungkinkan Anda melakukan ini.
Anda harus pragmatis tentang hal itu dan Anda juga harus mengetahui alasan mengapa hal-hal sulit untuk diuji.
' Dengarkan tesnya ' - jika sulit untuk diuji, apakah itu memberi tahu Anda tentang desain Anda? Bisakah Anda refactor ke tempat tes untuk metode ini akan sepele dan mudah ditutup dengan pengujian melalui api publik?
Inilah yang dikatakan Michael Feathers dalam ' Bekerja Secara Efektif dengan Kode Warisan "
sumber
Seperti yang dikatakan orang lain, agaknya dicurigai sebagai unit yang menguji metode pribadi sama sekali; unit menguji antarmuka publik, bukan detail implementasi pribadi.
Yang mengatakan, teknik yang saya gunakan ketika saya ingin menguji unit sesuatu yang bersifat pribadi di C # adalah menurunkan perlindungan aksesibilitas dari pribadi ke internal, dan kemudian menandai unit pengujian unit sebagai teman perakitan menggunakan InternalsVisibleTo . Unit pengujian unit kemudian akan diizinkan untuk memperlakukan internal sebagai publik, tetapi Anda tidak perlu khawatir tentang penambahan tidak sengaja ke area permukaan publik Anda.
sumber
Banyak jawaban menyarankan hanya menguji antarmuka publik, tetapi IMHO ini tidak realistis - jika metode melakukan sesuatu yang membutuhkan 5 langkah, Anda akan ingin menguji lima langkah secara terpisah, tidak semuanya bersamaan. Ini membutuhkan pengujian kelima metode, yang (selain untuk pengujian) mungkin sebaliknya
private
.Cara biasa untuk menguji metode "pribadi" adalah memberikan setiap kelas antarmuka sendiri, dan membuat metode "pribadi"
public
, tetapi tidak memasukkannya ke dalam antarmuka. Dengan cara ini, mereka masih dapat diuji, tetapi mereka tidak mengasapi antarmuka.Ya, ini akan menghasilkan file dan class-mengasapi.
Ya, ini memang membuat
public
danprivate
penspesifikasi berlebihan.Ya, ini menyebalkan.
Sayangnya, ini adalah salah satu dari banyak pengorbanan yang kami lakukan untuk membuat kode dapat diuji . Mungkin bahasa yang akan datang (atau bahkan versi C # / Java yang akan datang) akan memiliki fitur untuk membuat kemudahan uji kelas dan modul; tetapi sementara itu, kita harus melewati rintangan ini.
Ada beberapa yang berpendapat bahwa masing-masing langkah itu harus kelasnya sendiri , tetapi saya tidak setuju - jika semuanya berbagi keadaan, tidak ada alasan untuk membuat lima kelas terpisah di mana lima metode akan dilakukan. Lebih buruk lagi, ini menghasilkan file dan kelas mengasapi. Selain itu, ia menginfeksi API publik dari modul Anda - semua kelas tersebut harusnya pasti
public
jika Anda ingin mengujinya dari modul lain (baik itu, atau memasukkan kode uji dalam modul yang sama, yang berarti mengirim kode uji dengan produk Anda) .sumber
Tes unit harus menguji kontrak publik, satu-satunya cara bagaimana kelas dapat digunakan di bagian lain dari kode. Metode pribadi adalah detail implementasi, Anda tidak boleh mengujinya, sejauh API publik berfungsi dengan benar, penerapannya tidak masalah dan dapat diubah tanpa perubahan dalam kasus pengujian.
sumber
IMO, Anda harus menulis tes Anda tidak membuat asumsi mendalam tentang bagaimana kelas Anda diimplementasikan di dalam. Anda mungkin ingin memperbaiki nanti menggunakan model internal lain tetapi masih membuat jaminan yang sama dengan yang diberikan implementasi sebelumnya.
Ingatlah itu, saya menyarankan Anda untuk fokus pada pengujian bahwa kontrak Anda masih berlaku tidak peduli apa pun implementasi internal kelas Anda saat ini. Pengujian berbasis properti API publik Anda.
sumber
Bagaimana kalau menjadikannya paket pribadi? Kemudian kode pengujian Anda dapat melihatnya (dan kelas-kelas lain di paket Anda juga), tetapi masih tersembunyi dari pengguna Anda.
Tapi sungguh, Anda seharusnya tidak menguji metode pribadi. Itu adalah rincian implementasi, dan bukan bagian dari kontrak. Semua yang mereka lakukan harus ditutupi dengan memanggil metode publik (jika mereka memiliki kode di sana yang tidak dilakukan oleh metode publik, maka itu harus pergi). Jika kode pribadi terlalu rumit, kelas mungkin melakukan terlalu banyak hal dan ingin melakukan refactoring.
Membuat metode publik adalah komitmen besar. Setelah Anda melakukannya, orang-orang akan dapat menggunakannya, dan Anda tidak bisa mengubahnya lagi.
sumber
Pembaruan : Saya telah menambahkan jawaban yang lebih luas dan lebih lengkap untuk pertanyaan ini di banyak tempat lain. Ini dapat ditemukan di blog saya .
Jika saya perlu mempublikasikan sesuatu untuk mengujinya, ini biasanya mengisyaratkan bahwa sistem yang sedang diuji tidak mengikuti Prinsip Single Reponsibility . Karenanya ada kelas yang hilang yang harus diperkenalkan. Setelah mengekstraksi kode ke kelas baru, buat itu publik. Sekarang Anda dapat menguji dengan mudah, dan Anda mengikuti SRP. Kelas Anda yang lain hanya perlu memanggil kelas baru ini melalui komposisi.
Membuat metode publik / menggunakan trik langauge seperti menandai kode yang terlihat untuk menguji majelis harus selalu menjadi pilihan terakhir.
Sebagai contoh:
Perbaiki ini dengan memperkenalkan objek validator.
Sekarang yang harus kita lakukan adalah menguji apakah validator dipanggil dengan benar. Proses validasi aktual (logika pribadi sebelumnya) dapat diuji dalam isolasi murni. Tidak perlu untuk pengujian rumit yang diatur untuk memastikan validasi ini berlalu.
sumber
Beberapa jawaban bagus. Satu hal yang saya tidak lihat disebutkan adalah bahwa dengan pengembangan uji-didorong (TDD), metode pribadi dibuat selama fase refactoring (lihat Metode Ekstrak untuk contoh pola refactoring), dan karenanya harus sudah memiliki cakupan pengujian yang diperlukan . Jika dilakukan dengan benar (dan tentu saja, Anda akan mendapatkan kumpulan pendapat yang beragam ketika datang ke kebenaran), Anda tidak perlu khawatir tentang harus membuat metode pribadi publik hanya agar Anda dapat mengujinya.
sumber
Mengapa tidak membagi algoritma manajemen tumpukan ke dalam kelas utilitas? Kelas utilitas dapat mengelola tumpukan dan menyediakan aksesor publik. Tes unitnya dapat difokuskan pada detail implementasi. Tes mendalam untuk kelas yang rumit secara algoritmik sangat membantu dalam menghilangkan kasus tepi dan memastikan jangkauan.
Kemudian kelas Anda saat ini dapat dengan bersih mendelegasikan ke kelas utilitas tanpa mengekspos detail implementasi. Tesnya akan berhubungan dengan persyaratan pagination seperti yang direkomendasikan orang lain.
sumber
Di java, ada juga opsi untuk menjadikannya paket pribadi (yaitu meninggalkan pengubah visibilitas). Jika pengujian unit Anda berada dalam paket yang sama dengan kelas yang diuji, maka Anda harus dapat melihat metode ini, dan sedikit lebih aman daripada membuat metode tersebut sepenuhnya publik.
sumber
Metode pribadi biasanya digunakan sebagai metode "pembantu". Oleh karena itu mereka hanya mengembalikan nilai-nilai dasar dan tidak pernah beroperasi pada instance objek tertentu.
Anda memiliki beberapa opsi jika ingin mengujinya.
Atau Anda bisa membuat kelas baru dengan metode helper sebagai metode publik jika itu adalah kandidat yang cukup baik untuk kelas baru.
Ada artikel yang sangat bagus di sini tentang ini.
sumber
Jika Anda menggunakan C #, Anda dapat membuat metode internal. Dengan begitu Anda tidak mencemari API publik.
Kemudian tambahkan atribut ke dll
[perakitan: InternalsVisibleTo ("MyTestAssembly")]
Sekarang semua metode terlihat di proyek MyTestAssembly Anda. Mungkin tidak sempurna, tetapi lebih baik daripada membuat metode pribadi publik hanya untuk mengujinya.
sumber
Gunakan refleksi untuk mengakses variabel pribadi jika perlu.
Tapi sungguh, Anda tidak peduli dengan keadaan internal kelas, Anda hanya ingin menguji bahwa metode publik mengembalikan apa yang Anda harapkan dalam situasi yang dapat Anda antisipasi.
sumber
Saya akan mengatakan itu adalah ide yang buruk karena saya tidak yakin apakah Anda mendapatkan manfaat dan berpotensi masalah di telepon. Jika Anda mengubah kontrak panggilan, hanya untuk menguji metode pribadi, Anda tidak menguji kelas tentang bagaimana itu akan digunakan, tetapi membuat skenario buatan yang tidak pernah Anda inginkan terjadi.
Lebih jauh lagi, dengan mendeklarasikan metode sebagai publik, apa yang harus dikatakan bahwa dalam waktu enam bulan (setelah lupa bahwa satu-satunya alasan untuk membuat metode publik adalah untuk pengujian) bahwa Anda (atau jika Anda telah menyerahkan proyek) seseorang yang sama sekali berbeda akan menang. dapat menggunakannya, yang mengarah ke konsekuensi yang mungkin tidak diinginkan dan / atau mimpi buruk pemeliharaan.
sumber
dalam hal pengujian unit, Anda seharusnya tidak menambahkan lebih banyak metode; Saya percaya Anda akan lebih baik membuat test case tentang
first()
metode Anda , yang akan dipanggil sebelum setiap tes; maka Anda dapat memanggil beberapa kali -next()
,previous()
danlast()
untuk melihat apakah hasilnya sesuai dengan harapan Anda. Saya kira jika Anda tidak menambahkan lebih banyak metode ke kelas Anda (hanya untuk tujuan pengujian), Anda akan tetap berpegang pada prinsip pengujian "kotak hitam";sumber
Pertama lihat apakah metode ini harus diekstraksi ke kelas lain dan dipublikasikan. Jika bukan itu masalahnya, buatlah paket itu terlindungi dan di Java beri keterangan dengan @VisibleForTesting .
sumber
Dalam pembaruan Anda, Anda mengatakan bahwa baik untuk hanya menguji menggunakan API publik. Sebenarnya ada dua sekolah di sini.
Pengujian kotak hitam
Black box school mengatakan bahwa kelas harus dianggap sebagai kotak hitam sehingga tidak ada yang bisa melihat implementasi di dalamnya. Satu-satunya cara untuk menguji ini adalah melalui API publik - sama seperti pengguna kelas akan menggunakannya.
pengujian kotak putih.
Sekolah white box berpikir secara alami untuk menggunakan pengetahuan tentang implementasi kelas dan kemudian menguji kelas untuk mengetahui bahwa itu berfungsi sebagaimana mestinya.
Saya benar-benar tidak bisa memihak diskusi. Saya hanya berpikir itu akan menarik untuk mengetahui bahwa ada dua cara berbeda untuk menguji kelas (atau perpustakaan atau apa pun).
sumber
Sebenarnya ada situasi ketika Anda harus melakukan ini (misalnya ketika Anda menerapkan beberapa algoritma yang kompleks). Lakukan saja paket-pribadi dan ini sudah cukup. Tetapi dalam kebanyakan kasus mungkin Anda memiliki kelas yang terlalu rumit yang membutuhkan pemfaktoran logika ke dalam kelas lain.
sumber
Metode pribadi yang ingin Anda uji dalam isolasi adalah indikasi bahwa ada "konsep" lain yang terkubur di kelas Anda. Ekstrak "konsep" itu ke kelasnya sendiri dan uji itu sebagai "unit" yang terpisah.
Lihatlah video ini untuk melihat subjek yang sangat menarik.
sumber
Anda seharusnya tidak pernah membiarkan tes Anda menentukan kode Anda. Saya tidak berbicara tentang TDD atau DD lain yang saya maksud, persis apa yang Anda minta. Apakah aplikasi Anda memerlukan metode itu untuk umum. Jika tidak maka uji mereka. Jika tidak maka jangan dipublikasikan untuk pengujian. Sama dengan variabel dan lainnya. Biarkan kebutuhan aplikasi Anda mendikte kode, dan biarkan tes Anda menguji bahwa kebutuhan terpenuhi. (Sekali lagi saya tidak bermaksud menguji dulu atau tidak maksud saya mengubah struktur kelas untuk memenuhi tujuan pengujian).
Sebaliknya Anda harus "menguji lebih tinggi". Uji metode yang memanggil metode pribadi. Tetapi pengujian Anda harus menguji kebutuhan aplikasi Anda dan bukan "keputusan implementasi" Anda.
Misalnya (kode bod pseudo di sini);
Tidak ada alasan untuk menguji "tambah", Anda dapat menguji "buku".
Jangan pernah membiarkan tes Anda membuat keputusan desain kode untuk Anda. Uji bahwa Anda mendapatkan hasil yang diharapkan, bukan bagaimana Anda mendapatkan hasil itu.
sumber
Saya cenderung setuju bahwa bonus dari unit yang diuji lebih besar daripada masalah meningkatkan visibilitas beberapa anggota. Sedikit perbaikan adalah membuatnya terlindungi dan virtual, lalu menimpanya di kelas uji untuk mengeksposnya.
Atau, jika itu fungsionalitas yang ingin Anda uji secara terpisah, apakah itu tidak menyarankan objek yang hilang dari desain Anda? Mungkin Anda bisa meletakkannya di kelas yang dapat diuji ... maka kelas yang ada hanya mendelegasikan ke instance kelas baru ini.
sumber
Saya biasanya menyimpan kelas tes dalam proyek / perakitan yang sama dengan kelas yang diuji.
Dengan cara ini saya hanya perlu
internal
visibilitas untuk membuat fungsi / kelas dapat diuji.Ini agak menyulitkan proses pembangunan Anda, yang perlu menyaring kelas tes. Saya mencapai ini dengan memberi nama semua kelas pengujian saya
TestedClassTest
dan menggunakan regex untuk menyaring kelas-kelas itu.Ini tentu saja hanya berlaku untuk bagian C # / .NET dari pertanyaan Anda
sumber
Aku akan sering menambahkan metode yang disebut sesuatu seperti
validate
,verify
,check
, dll, untuk kelas sehingga bisa disebut untuk menguji keadaan internal suatu objek.Kadang-kadang metode ini dibungkus dengan blok ifdef (saya menulis sebagian besar dalam C ++) sehingga tidak dikompilasi untuk rilis. Tetapi sering berguna dalam rilis untuk memberikan metode validasi yang berjalan pohon objek program memeriksa hal-hal.
sumber
Guava memiliki anotasi @VisibleForTesting untuk metode penandaan yang memiliki cakupan yang diperbesar (paket atau publik) yang sebaliknya. Saya menggunakan anotasi @Private untuk hal yang sama.
Meskipun API publik harus diuji, kadang-kadang nyaman dan masuk akal untuk mendapatkan hal-hal yang biasanya tidak bersifat publik.
Kapan:
sepertinya agama adalah rekayasa yang sebenarnya.
sumber
Saya biasanya meninggalkan metode-metode tersebut
protected
dan menempatkan unit test dalam paket yang sama (tetapi dalam proyek atau folder sumber lain), di mana mereka dapat mengakses semua metode yang dilindungi karena class loader akan menempatkan mereka dalam namespace yang sama.sumber
Tidak, karena ada cara yang lebih baik untuk menguliti kucing itu.
Beberapa harness uji unit bergantung pada makro dalam definisi kelas yang secara otomatis diperluas untuk membuat kait ketika dibangun dalam mode uji. Gaya sangat C, tapi berhasil.
Idiom OO yang lebih mudah adalah membuat apa pun yang Anda ingin uji "dilindungi" daripada "pribadi". Rangkaian uji mewarisi dari kelas yang diuji, dan kemudian dapat mengakses semua anggota yang dilindungi.
Atau Anda memilih opsi "teman". Secara pribadi ini adalah fitur C ++ saya paling tidak suka karena melanggar aturan enkapsulasi, tetapi kebetulan diperlukan untuk bagaimana C ++ mengimplementasikan beberapa fitur, jadi hei ho.
Lagi pula, jika Anda menguji unit maka kemungkinan besar Anda harus menyuntikkan nilai ke anggota tersebut. Texting kotak putih sangat valid. Dan itu benar-benar akan merusak enkapsulasi Anda.
sumber
Di .Net ada kelas khusus yang disebut
PrivateObject
deigned khusus untuk memungkinkan Anda mengakses metode pribadi kelas.Lihat lebih lanjut di MSDN atau di sini di Stack Overflow
(Saya ingin tahu bahwa belum ada yang menyebutkannya sejauh ini.)
Ada beberapa situasi yang tidak cukup, dalam hal ini Anda harus menggunakan refleksi.
Tetap saya akan mematuhi rekomendasi umum untuk tidak menguji metode pribadi, namun seperti biasa selalu ada pengecualian.
sumber
Seperti yang banyak dicatat oleh komentar orang lain, unit test harus fokus pada API publik. Namun, selain pro dan kontra, Anda dapat memanggil metode pribadi dalam tes unit dengan menggunakan refleksi. Anda tentu saja perlu memastikan keamanan JRE Anda mengizinkannya. Memanggil metode pribadi adalah sesuatu yang digunakan oleh Spring Framework dengan ReflectionUtils itu (lihat
makeAccessible(Method)
metode).Berikut adalah contoh kelas kecil dengan metode instance pribadi.
Dan contoh kelas yang mengeksekusi metode instance pribadi.
Executing B, akan mencetak
Doing something private.
Jika Anda benar-benar membutuhkannya, refleksi dapat digunakan dalam unit test untuk mengakses metode instance pribadi.sumber
Secara pribadi, saya memiliki masalah yang sama ketika menguji metode pribadi dan ini karena beberapa alat pengujian terbatas. Desain Anda tidak baik didorong oleh alat yang terbatas jika tidak menanggapi kebutuhan Anda, ubah alat, bukan desain. Karena Anda meminta C # i tidak dapat mengusulkan alat pengujian yang baik, tetapi untuk Java ada dua alat yang kuat: TestNG dan PowerMock, dan Anda dapat menemukan alat pengujian yang sesuai untuk platform .NET
sumber