Saya telah membaca posting ini tentang cara menguji metode pribadi. Saya biasanya tidak menguji mereka, karena saya selalu berpikir lebih cepat untuk menguji hanya metode publik yang akan dipanggil dari luar objek. Apakah Anda menguji metode pribadi? Haruskah saya selalu mengujinya?
unit-testing
testing
language-agnostic
Patrick Desjardins
sumber
sumber
Jawaban:
Saya tidak menguji metode pribadi unit. Metode pribadi adalah detail implementasi yang harus disembunyikan bagi pengguna kelas. Pengujian metode pribadi memecah enkapsulasi.
Jika saya menemukan bahwa metode pribadi sangat besar atau kompleks atau cukup penting untuk memerlukan tes sendiri, saya hanya meletakkannya di kelas lain dan membuatnya publik di sana ( Metode Objek ). Kemudian saya dapat dengan mudah menguji metode yang sebelumnya bersifat pribadi tetapi sekarang publik yang sekarang hidup di kelasnya sendiri.
sumber
Apa tujuan pengujian?
Mayoritas jawaban sejauh ini mengatakan bahwa metode pribadi adalah detail implementasi yang tidak (atau setidaknya tidak seharusnya) menjadi masalah selama antarmuka publik teruji dan berfungsi dengan baik. Itu benar sekali jika satu-satunya tujuan pengujian Anda adalah untuk memastikan bahwa antarmuka publik berfungsi .
Secara pribadi, penggunaan utama saya untuk pengujian kode adalah untuk memastikan bahwa perubahan kode di masa depan tidak menimbulkan masalah dan untuk membantu upaya debugging saya jika mereka melakukannya. Saya menemukan bahwa pengujian metode pribadi sama baiknya dengan antarmuka publik (jika tidak lebih!) Lebih lanjut tujuan itu.
Pertimbangkan: Anda memiliki metode publik A yang memanggil metode pribadi B. A dan B keduanya menggunakan metode C. C diubah (mungkin oleh Anda, mungkin oleh vendor), menyebabkan A mulai gagal dalam pengujiannya. Bukankah bermanfaat untuk memiliki tes untuk B juga, meskipun itu bersifat pribadi, sehingga Anda tahu apakah masalahnya adalah dalam penggunaan A dari C, penggunaan B dari C, atau keduanya?
Menguji metode pribadi juga menambah nilai dalam kasus di mana cakupan pengujian antarmuka publik tidak lengkap. Meskipun ini adalah situasi yang umumnya ingin kami hindari, pengujian unit efisiensi tergantung pada pengujian yang menemukan bug dan biaya pengembangan serta perawatan yang terkait dari pengujian tersebut. Dalam beberapa kasus, manfaat dari cakupan pengujian 100% mungkin dinilai tidak cukup untuk menjamin biaya pengujian tersebut, menghasilkan kesenjangan dalam cakupan pengujian antarmuka publik. Dalam kasus seperti itu, pengujian metode pribadi yang ditargetkan dengan baik dapat menjadi tambahan yang sangat efektif untuk basis kode.
sumber
testDoSomething()
atautestDoSomethingPrivate()
. Ini membuat tes kurang berharga. . Berikut ini lebih banyak alasan untuk menguji stackoverflow.com/questions/34571/… pribadi :Saya cenderung mengikuti saran Dave Thomas dan Andy Hunt dalam buku mereka Pragmatic Unit Testing :
Tetapi kadang-kadang saya tidak dapat menghentikan diri saya dari pengujian metode pribadi karena itu memberi saya rasa meyakinkan bahwa saya sedang membangun program yang benar - benar kuat.
sumber
Saya agak merasa terdorong untuk menguji fungsi-fungsi pribadi karena saya semakin banyak mengikuti salah satu rekomendasi QA terbaru kami dalam proyek kami:
Sekarang efek samping dari penegakan kebijakan ini adalah banyak dari fungsi publik saya yang sangat besar terbagi dalam banyak fungsi pribadi yang lebih terfokus dan lebih baik .
Fungsi publik masih ada (tentu saja) tetapi pada dasarnya direduksi menjadi yang disebut semua 'subfungsi' pribadi
Itu sebenarnya keren, karena callstack sekarang jauh lebih mudah dibaca (daripada bug dalam fungsi besar, saya punya bug di sub-sub-fungsi dengan nama fungsi sebelumnya di callstack untuk membantu saya memahami 'bagaimana saya sampai di sana')
Namun, sekarang tampaknya lebih mudah untuk menguji secara langsung fungsi - fungsi pribadi tersebut , dan menyerahkan pengujian fungsi publik yang besar ke semacam tes 'integrasi' di mana skenario perlu ditangani.
Hanya 2 sen saya.
sumber
Ya saya melakukan tes fungsi pribadi, karena meskipun mereka diuji dengan metode publik Anda, itu bagus di TDD (Test Driven Design) untuk menguji bagian terkecil dari aplikasi. Tetapi fungsi pribadi tidak dapat diakses ketika Anda berada di kelas unit tes Anda. Inilah yang kami lakukan untuk menguji metode pribadi kami.
Mengapa kita memiliki metode pribadi?
Fungsi privat terutama ada di kelas kami karena kami ingin membuat kode yang dapat dibaca dalam metode publik kami. Kami tidak ingin pengguna kelas ini memanggil metode ini secara langsung, tetapi melalui metode publik kami. Juga, kami tidak ingin mengubah perilaku mereka ketika memperluas kelas (jika dilindungi), maka itu bersifat pribadi.
Saat kami membuat kode, kami menggunakan test-driven-design (TDD). Ini berarti bahwa kadang-kadang kita menemukan fungsi yang bersifat pribadi dan ingin diuji. Fungsi pribadi tidak dapat diuji di phpUnit, karena kami tidak dapat mengaksesnya di kelas Uji (mereka pribadi).
Kami pikir di sini ada 3 solusi:
1. Anda dapat menguji kemaluan Anda melalui metode publik Anda
Keuntungan
Kekurangan
2. Jika privat sangat penting, maka mungkin ini merupakan kode untuk membuat kelas baru yang terpisah
Keuntungan
Kekurangan
3. Ubah pengubah akses ke (final) yang diproteksi
Keuntungan
Kekurangan
Contoh
Jadi unit pengujian kami sekarang dapat memanggil test_sleepWithSuspect untuk menguji fungsi pribadi kami sebelumnya.
sumber
Saya tidak suka menguji fungsionalitas pribadi karena beberapa alasan. Mereka adalah sebagai berikut (ini adalah poin utama untuk orang-orang TLDR):
Saya akan menjelaskan masing-masing dengan contoh nyata. Ternyata 2) dan 3) agak terhubung secara rumit, jadi contoh mereka mirip, meskipun saya menganggap mereka alasan terpisah mengapa Anda tidak boleh menguji metode pribadi.
Ada kalanya menguji metode pribadi sesuai, hanya penting untuk mengetahui kelemahan yang tercantum di atas. Saya akan membahasnya lebih terinci nanti.
Saya juga membahas mengapa TDD bukan alasan yang valid untuk menguji metode pribadi di akhir.
Refactoring jalan keluar dari desain yang buruk
Salah satu pola (anti) yang paling umum yang saya lihat adalah apa yang Michael Feathers sebut sebagai kelas "Iceberg" (jika Anda tidak tahu siapa Michael Feathers, pergi membeli / baca bukunya "Bekerja Efektif dengan Kode Legacy". Dia adalah seseorang yang perlu diketahui jika Anda seorang insinyur / pengembang perangkat lunak profesional). Ada pola (anti) lain yang menyebabkan masalah ini muncul, tetapi sejauh ini yang paling umum yang pernah saya temui. Kelas "Gunung Es" memiliki satu metode publik, dan sisanya adalah privat (itulah sebabnya menggoda untuk menguji metode privat). Ini disebut kelas "Gunung Es" karena biasanya ada satu-satunya metode publik yang muncul, tetapi fungsi lainnya disembunyikan di bawah air dalam bentuk metode pribadi.
Misalnya, Anda mungkin ingin menguji
GetNextToken()
dengan menyebutnya pada string secara berturut-turut dan melihat bahwa itu mengembalikan hasil yang diharapkan. Fungsi seperti ini benar-benar memerlukan tes: perilaku itu tidak sepele, terutama jika aturan tokenizing Anda rumit. Mari kita berpura-pura tidak semua yang rumit, dan kita hanya ingin mengikat token dibatasi oleh ruang. Jadi Anda menulis tes, mungkin terlihat seperti ini (beberapa kode aguedostic bahasa, semoga idenya jelas):Yah, itu sebenarnya terlihat cukup bagus. Kami ingin memastikan kami mempertahankan perilaku ini saat kami membuat perubahan. Tetapi
GetNextToken()
adalah fungsi pribadi ! Jadi kami tidak dapat mengujinya seperti ini, karena ia bahkan tidak dapat dikompilasi (dengan asumsi kami menggunakan beberapa bahasa yang benar-benar menegakkan publik / pribadi, tidak seperti beberapa bahasa scripting seperti Python). Tetapi bagaimana dengan mengubahRuleEvaluator
kelas untuk mengikuti Prinsip Tanggung Jawab Tunggal (Single Responsibility Principle)? Sebagai contoh, kita tampaknya memiliki pengurai, tokenizer, dan evaluator macet ke dalam satu kelas. Bukankah lebih baik memisahkan tanggung jawab itu? Selain itu, jika Anda membuatTokenizer
kelas, maka metode publiknya adalahHasMoreTokens()
danGetNextTokens()
. TheRuleEvaluator
kelas bisa memilikiTokenizer
objek sebagai anggota. Sekarang, kita dapat menyimpan tes yang sama seperti di atas, kecuali kita mengujiTokenizer
kelas, bukanRuleEvaluator
kelas.Berikut ini tampilannya di UML:
Perhatikan bahwa desain baru ini meningkatkan modularitas, sehingga Anda dapat berpotensi menggunakan kembali kelas-kelas ini di bagian lain dari sistem Anda (sebelum Anda tidak bisa, metode pribadi tidak dapat digunakan kembali dengan definisi). Ini adalah keuntungan utama dari mematahkan RuleEvaluator, bersama dengan peningkatan pemahaman / lokalitas.
Tes akan terlihat sangat mirip, kecuali itu sebenarnya akan mengkompilasi kali ini karena
GetNextToken()
metode ini sekarang umum diTokenizer
kelas:Menguji komponen pribadi melalui antarmuka publik dan menghindari duplikasi pengujian
Bahkan jika Anda tidak berpikir Anda dapat memecah masalah Anda menjadi komponen modular yang lebih sedikit (yang Anda dapat 95% dari waktu jika Anda hanya mencoba untuk melakukannya), Anda dapat dengan mudah menguji fungsi pribadi melalui antarmuka publik. Sering kali anggota pribadi tidak layak diuji karena mereka akan diuji melalui antarmuka publik. Banyak kali yang saya lihat adalah tes yang terlihat sangat mirip, tetapi menguji dua fungsi / metode yang berbeda. Apa yang akhirnya terjadi adalah ketika persyaratan berubah (dan selalu terjadi), Anda sekarang memiliki 2 tes yang rusak alih-alih 1. Dan jika Anda benar-benar menguji semua metode pribadi Anda, Anda mungkin memiliki lebih dari 10 tes yang rusak daripada 1. Singkatnya , Menguji fungsi pribadi (dengan menggunakan
FRIEND_TEST
atau menjadikannya publik atau menggunakan refleksi) yang jika tidak dapat diuji melalui antarmuka publik dapat menyebabkan duplikasi pengujian . Anda benar-benar tidak menginginkan ini, karena tidak ada yang lebih menyakitkan daripada test suite Anda yang memperlambat Anda. Seharusnya mengurangi waktu pengembangan dan mengurangi biaya perawatan! Jika Anda menguji metode pribadi yang diuji melalui antarmuka publik, test suite mungkin melakukan sebaliknya, dan secara aktif meningkatkan biaya pemeliharaan dan meningkatkan waktu pengembangan. Saat Anda membuat fungsi pribadi menjadi publik, atau jika Anda menggunakan sesuatu sepertiFRIEND_TEST
dan / atau refleksi, Anda biasanya akan menyesalinya dalam jangka panjang.Pertimbangkan kemungkinan implementasi
Tokenizer
kelas berikut:Katakanlah yang
SplitUpByDelimiter()
bertanggung jawab untuk mengembalikan array sehingga setiap elemen dalam array adalah token. Lebih jauh, katakan saja ituGetNextToken()
hanyalah iterator pada vektor ini. Jadi tes publik Anda mungkin terlihat seperti ini:Mari kita berpura-pura memiliki apa yang disebut Michael Feather sebagai alat meraba - raba . Ini adalah alat yang memungkinkan Anda menyentuh bagian pribadi orang lain. Contohnya adalah
FRIEND_TEST
dari googletest, atau refleksi jika bahasa mendukungnya.Nah, sekarang katakanlah persyaratan berubah, dan tokenizing menjadi jauh lebih kompleks. Anda memutuskan bahwa pembatas string sederhana tidak akan cukup, dan Anda membutuhkan
Delimiter
kelas untuk menangani pekerjaan itu. Secara alami, Anda akan mengharapkan satu tes untuk istirahat, tetapi rasa sakit itu meningkat ketika Anda menguji fungsi pribadi.Kapan pengujian metode pribadi sesuai?
Tidak ada "satu ukuran untuk semua" dalam perangkat lunak. Terkadang tidak apa-apa (dan sebenarnya ideal) untuk "melanggar aturan". Saya sangat menganjurkan tidak menguji fungsionalitas pribadi ketika Anda bisa. Ada dua situasi utama ketika saya pikir tidak apa-apa:
Saya telah bekerja secara luas dengan sistem warisan (itulah sebabnya saya penggemar berat Michael Feathers), dan saya dapat dengan aman mengatakan bahwa kadang-kadang paling aman untuk hanya menguji fungsionalitas pribadi. Ini bisa sangat membantu untuk memasukkan "tes karakterisasi" ke baseline.
Anda sedang terburu-buru, dan harus melakukan hal tercepat untuk di sini dan sekarang. Dalam jangka panjang, Anda tidak ingin menguji metode pribadi. Tetapi saya akan mengatakan bahwa biasanya perlu waktu untuk refactor untuk mengatasi masalah desain. Dan kadang-kadang Anda harus mengirim dalam seminggu. Tidak apa-apa: lakukan yang cepat dan kotor dan uji metode pribadi menggunakan alat meraba jika itu yang Anda pikirkan adalah cara tercepat dan paling dapat diandalkan untuk menyelesaikan pekerjaan. Tetapi pahamilah bahwa apa yang Anda lakukan adalah suboptimal dalam jangka panjang, dan tolong pertimbangkan kembali ke sana (atau, jika dilupakan tetapi Anda melihatnya nanti, perbaiki).
Mungkin ada situasi lain di mana tidak apa-apa. Jika Anda pikir tidak apa-apa, dan Anda memiliki pembenaran yang baik, maka lakukanlah. Tidak ada yang menghentikan Anda. Sadarilah potensi biaya.
Alasan TDD
Selain itu, saya benar-benar tidak suka orang yang menggunakan TDD sebagai alasan untuk menguji metode pribadi. Saya berlatih TDD, dan saya tidak berpikir TDD memaksa Anda untuk melakukan ini. Anda dapat menulis tes Anda (untuk antarmuka publik Anda) terlebih dahulu, dan kemudian menulis kode untuk memenuhi antarmuka itu. Kadang-kadang saya menulis tes untuk antarmuka publik, dan saya akan memuaskannya dengan menulis satu atau dua metode pribadi yang lebih kecil juga (tapi saya tidak menguji metode pribadi secara langsung, tetapi saya tahu mereka berfungsi atau tes publik saya akan gagal ). Jika saya perlu menguji kasus tepi dari metode pribadi itu, saya akan menulis sejumlah besar tes yang akan mengenai mereka melalui antarmuka publik saya.Jika Anda tidak tahu cara menabrak casing tepi, ini adalah pertanda kuat yang Anda butuhkan untuk merefleksikan komponen kecil masing-masing dengan metode publik mereka sendiri. Ini pertanda bahwa fungsi pribadi Anda terlalu banyak, dan di luar ruang kelas .
Juga, kadang-kadang saya menemukan saya menulis tes yang terlalu besar untuk dikunyah saat ini, dan jadi saya pikir "eh saya akan kembali ke tes nanti ketika saya memiliki lebih banyak API untuk bekerja dengan" (saya akan berkomentar dan menyimpannya di pikiran saya). Di sinilah banyak devs yang saya temui kemudian akan mulai menulis tes untuk fungsionalitas pribadi mereka, menggunakan TDD sebagai kambing hitam. Mereka berkata "oh, baik saya perlu tes lain, tetapi untuk menulis tes itu, saya akan memerlukan metode pribadi ini. Oleh karena itu, karena saya tidak dapat menulis kode produksi tanpa menulis tes, saya perlu menulis tes untuk metode pribadi. " Tetapi apa yang benar-benar perlu mereka lakukan adalah refactoring menjadi komponen yang lebih kecil dan dapat digunakan kembali daripada menambahkan / menguji sekelompok metode pribadi ke kelas mereka saat ini.
catatan:
Saya menjawab pertanyaan serupa tentang pengujian metode pribadi menggunakan GoogleTest beberapa saat yang lalu. Saya sebagian besar memodifikasi jawaban itu menjadi lebih banyak bahasa agnostik di sini.
PS Inilah kuliah yang relevan tentang kelas gunung es dan alat meraba oleh Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU
sumber
_
, itu menandakan "hei, ini 'pribadi'. Anda dapat menggunakannya, tetapi pengungkapan penuh, itu tidak dirancang untuk digunakan kembali dan Anda hanya boleh menggunakannya jika Anda benar-benar tahu apa yang kamu lakukan ". Anda dapat mengambil pendekatan yang sama dalam bahasa apa pun: buat anggota tersebut terbuka untuk umum, tetapi tandai sebagai pemimpin_
. Atau mungkin fungsi-fungsi itu harus bersifat pribadi, dan baru saja diuji melalui antarmuka publik (lihat jawaban untuk detail lebih lanjut). Ini adalah kasus per kasus, tidak ada aturan umumSaya pikir yang terbaik adalah hanya menguji antarmuka publik dari suatu objek. Dari sudut pandang dunia luar, hanya perilaku antarmuka publik yang penting dan inilah yang harus diarahkan oleh unit Anda.
Setelah Anda memiliki beberapa tes unit solid yang ditulis untuk objek Anda tidak ingin harus kembali dan mengubah tes hanya karena implementasi di belakang antarmuka berubah. Dalam situasi ini, Anda telah merusak konsistensi pengujian unit Anda.
sumber
Jika metode pribadi Anda tidak diuji dengan memanggil metode publik Anda, lalu apa yang dilakukannya? Saya berbicara pribadi tidak dilindungi atau teman.
sumber
Jika metode pribadi didefinisikan dengan baik (yaitu, ia memiliki fungsi yang dapat diuji dan tidak dimaksudkan untuk berubah dari waktu ke waktu) maka ya. Saya menguji semua yang dapat diuji di tempat yang masuk akal.
Misalnya, pustaka enkripsi mungkin menyembunyikan fakta bahwa ia melakukan blok enkripsi dengan metode pribadi yang mengenkripsi hanya 8 byte pada suatu waktu. Saya akan menulis tes unit untuk itu - itu tidak dimaksudkan untuk berubah, meskipun itu tersembunyi, dan jika itu rusak (karena peningkatan kinerja di masa depan, misalnya) maka saya ingin tahu bahwa itu adalah fungsi pribadi yang rusak, bukan hanya bahwa salah satu fungsi publik rusak.
Ini mempercepat debugging nanti.
-Adam
sumber
Jika Anda mengembangkan test driven (TDD), Anda akan menguji metode pribadi Anda.
sumber
Saya bukan ahli dalam bidang ini, tetapi pengujian unit harus menguji perilaku, bukan implementasi. Metode privat adalah bagian dari implementasi, jadi IMHO tidak boleh diuji.
sumber
Kami menguji metode pribadi dengan inferensi, yang saya maksud kami mencari cakupan tes kelas total minimal 95%, tetapi hanya meminta tes kami memanggil metode publik atau internal. Untuk mendapatkan liputan, kita perlu melakukan beberapa panggilan ke publik / internal berdasarkan berbagai skenario yang mungkin terjadi. Ini membuat pengujian kami lebih disengaja di sekitar tujuan kode yang mereka uji.
Jawaban Trumpi untuk pos yang Anda tautkan adalah yang terbaik.
sumber
Tes unit saya percaya adalah untuk menguji metode publik. Metode publik Anda menggunakan metode pribadi Anda, sehingga secara tidak langsung mereka juga diuji.
sumber
Saya telah mengatasi masalah ini untuk sementara waktu terutama dengan mencoba tangan saya di TDD.
Saya telah menemukan dua posting yang menurut saya cukup untuk mengatasi masalah ini dalam kasus TDD.
Singkatnya:
Saat menggunakan teknik pengembangan yang didorong (desain), metode pribadi harus muncul hanya selama proses re-factoring dari kode yang sudah bekerja dan diuji.
Sesuai dengan sifat prosesnya, sedikit saja fungsi implementasi sederhana yang diekstraksi dari fungsi yang diuji secara menyeluruh akan diuji sendiri (yaitu cakupan pengujian tidak langsung).
Bagi saya tampaknya cukup jelas bahwa pada bagian awal pengkodean sebagian besar metode akan menjadi fungsi tingkat yang lebih tinggi karena mereka merangkum / menggambarkan desain.
Oleh karena itu, metode ini akan dipublikasikan dan mengujinya akan cukup mudah.
Metode pribadi akan datang nanti setelah semuanya bekerja dengan baik dan kami sedang mempertimbangkan untuk keterbacaan dan kebersihan .
sumber
Seperti dikutip di atas, "Jika Anda tidak menguji metode pribadi Anda, bagaimana Anda tahu mereka tidak akan rusak?"
Ini adalah masalah besar. Salah satu poin utama dari unit test adalah untuk mengetahui di mana, kapan, dan bagaimana sesuatu pecah ASAP. Sehingga mengurangi sejumlah besar upaya pengembangan & QA. Jika semua yang diuji adalah publik, maka Anda tidak memiliki liputan yang jujur dan penggambaran internal kelas.
Saya telah menemukan salah satu cara terbaik untuk melakukan ini adalah cukup menambahkan referensi tes ke proyek dan menempatkan tes dalam kelas yang sejajar dengan metode pribadi. Masukkan logika build yang sesuai sehingga tes tidak masuk ke dalam tugas akhir.
Maka Anda memiliki semua manfaat memiliki metode ini diuji dan Anda dapat menemukan masalah dalam hitungan detik versus menit atau jam.
Jadi secara ringkas, ya, unit uji metode pribadi Anda.
sumber
Anda seharusnya tidak . Jika metode pribadi Anda memiliki cukup kompleksitas yang harus diuji, Anda harus meletakkannya di kelas lain. Jaga kohesi yang tinggi , sebuah kelas seharusnya hanya memiliki satu tujuan. Antarmuka publik kelas harus cukup.
sumber
Jika Anda tidak menguji metode pribadi Anda, bagaimana Anda tahu mereka tidak akan rusak?
sumber
Ini jelas tergantung pada bahasa. Di masa lalu dengan c ++, saya telah menyatakan kelas pengujian sebagai kelas teman. Sayangnya, ini membutuhkan kode produksi Anda untuk mengetahui tentang kelas pengujian.
sumber
Saya memahami sudut pandang di mana metode pribadi dianggap sebagai detail implementasi dan kemudian tidak harus diuji. Dan saya akan tetap dengan aturan ini jika kita harus mengembangkan di luar objek saja. Tapi kami, apakah kami semacam pengembang terbatas yang hanya mengembangkan di luar objek, hanya menyebut metode publik mereka? Atau apakah kita sebenarnya juga mengembangkan objek itu? Karena kita tidak terikat untuk memprogram objek luar, kita mungkin harus memanggil metode privat itu menjadi metode publik baru yang sedang kita kembangkan. Bukankah lebih baik mengetahui bahwa metode pribadi melawan segala rintangan?
Saya tahu beberapa orang dapat menjawab bahwa jika kita sedang mengembangkan metode publik lain ke objek itu maka yang ini harus diuji dan hanya itu (metode pribadi dapat melanjutkan hidup tanpa tes). Tetapi ini juga berlaku untuk metode publik suatu objek: ketika mengembangkan aplikasi web, semua metode publik suatu objek dipanggil dari metode pengontrol dan karenanya dapat dianggap sebagai detail implementasi untuk pengontrol.
Jadi mengapa kita menguji objek unit? Karena ini sangat sulit, tidak mustahil untuk memastikan bahwa kami menguji metode controller dengan input yang sesuai yang akan memicu semua cabang kode yang mendasarinya. Dengan kata lain, semakin tinggi kita berada di tumpukan, semakin sulit untuk menguji semua perilaku. Demikian juga dengan metode pribadi.
Bagi saya batas antara metode pribadi dan publik adalah kriteria psikologis dalam hal tes. Kriteria yang lebih penting bagi saya adalah:
sumber
Jika saya menemukan bahwa metode pribadi sangat besar atau kompleks atau cukup penting untuk memerlukan tes sendiri, saya hanya meletakkannya di kelas lain dan membuatnya publik di sana (Metode Objek). Kemudian saya dapat dengan mudah menguji metode publik yang sebelumnya pribadi tetapi sekarang yang hidup di kelasnya sendiri.
sumber
Saya tidak pernah mengerti konsep Unit Test tetapi sekarang saya tahu apa tujuannya.
Tes Unit bukan tes lengkap . Jadi, ini bukan pengganti untuk QA dan tes manual. Konsep TDD dalam aspek ini salah karena Anda tidak dapat menguji semuanya, termasuk metode pribadi tetapi juga, metode yang menggunakan sumber daya (terutama sumber daya yang tidak kami kontrol). TDD mendasarkan semua kualitasnya adalah sesuatu yang tidak dapat dicapai.
Tes Unit lebih merupakan tes pivot. Anda menandai pivot yang sewenang-wenang dan hasil pivot harus tetap sama.
sumber
Publik vs swasta bukan perbedaan yang berguna untuk panggilan apis dari tes Anda, juga metode vs kelas. Sebagian besar unit yang dapat diuji terlihat dalam satu konteks, tetapi tersembunyi dalam yang lain.
Yang penting adalah cakupan dan biaya. Anda perlu meminimalkan biaya sambil mencapai sasaran cakupan proyek Anda (garis, cabang, jalur, blok, metode, kelas, kelas ekivalensi, kasus penggunaan ... apa pun yang diputuskan tim).
Jadi gunakan alat untuk memastikan cakupan, dan rancang pengujian Anda untuk menimbulkan biaya paling kecil (jangka pendek dan jangka panjang ).
Jangan membuat tes lebih mahal dari yang diperlukan. Jika termurah hanya menguji titik masuk publik lakukan itu. Jika termurah untuk menguji metode pribadi, lakukan itu.
Ketika Anda semakin berpengalaman, Anda akan menjadi lebih baik dalam memprediksi kapan layak refactoring untuk menghindari biaya jangka panjang pemeliharaan tes.
sumber
Jika metode ini cukup signifikan / kompleks, saya biasanya akan membuatnya "dilindungi" dan mengujinya. Beberapa metode akan dibiarkan pribadi dan diuji secara implisit sebagai bagian dari pengujian unit untuk metode publik / terlindungi.
sumber
Saya melihat banyak orang berada dalam jalur pemikiran yang sama: ujian di tingkat publik. tapi bukankah itu yang dilakukan tim QA kita? Mereka menguji input dan output yang diharapkan. Jika sebagai pengembang kami hanya menguji metode publik maka kami hanya mengulangi pekerjaan QA dan tidak menambahkan nilai apa pun dengan "pengujian unit".
sumber
Jawaban untuk "Haruskah saya menguji metode pribadi?" adalah "....... terkadang". Biasanya Anda harus menguji terhadap antarmuka kelas Anda.
Berikut ini sebuah contoh:
Dalam
RefactoredThing
Anda sekarang memiliki 5 tes, 2 dari yang Anda harus memperbarui untuk refactoring, tapi fungsi objek Anda benar-benar tidak berubah. Jadi katakanlah hal-hal lebih kompleks dari itu dan Anda memiliki beberapa metode yang mendefinisikan urutan output seperti:Ini seharusnya tidak dijalankan oleh pengguna luar, tetapi kelas enkapsulasi Anda mungkin terlalu berat untuk menjalankan banyak logika melewatinya berulang kali. Dalam hal ini mungkin Anda lebih suka mengekstraksi ini ke dalam kelas yang terpisah, memberikan kelas itu sebuah antarmuka dan mengujinya.
Dan akhirnya, katakanlah objek utama Anda sangat berat, dan metodenya cukup kecil dan Anda benar-benar perlu memastikan bahwa outputnya benar. Anda berpikir, "Saya harus menguji metode pribadi ini!". Pernahkah Anda bahwa Anda dapat membuat objek Anda lebih ringan dengan melewatkan beberapa pekerjaan berat sebagai parameter inisialisasi? Kemudian Anda dapat melewatkan sesuatu yang lebih ringan dan mengujinya.
sumber
Tidak. Anda tidak boleh menguji Metode Pribadi mengapa? dan apalagi kerangka kerja mengejek yang populer seperti Mockito tidak memberikan dukungan untuk pengujian metode pribadi.
sumber
Satu poin utama adalah
Jika kita menguji untuk memastikan kebenaran logika, dan metode pribadi membawa logika, kita harus mengujinya. Bukan? Jadi mengapa kita akan melewatkan itu?
Menulis tes berdasarkan visibilitas metode adalah ide yang sama sekali tidak relevan.
sebaliknya
Di sisi lain, memanggil metode pribadi di luar kelas asli adalah masalah utama. Dan juga ada batasan untuk mengejek metode pribadi dalam beberapa alat mengejek. (Mis: Mockito )
Meskipun ada beberapa alat seperti Power Mock yang mendukungnya, itu adalah operasi yang berbahaya. Alasannya adalah perlu meretas JVM untuk mencapai itu.
Satu pekerjaan di sekitar yang dapat dilakukan adalah (Jika Anda ingin menulis kasus uji untuk metode pribadi)
Nyatakan metode pribadi itu sebagai terlindungi . Tetapi mungkin tidak nyaman untuk beberapa situasi.
sumber
Ini bukan hanya tentang metode atau fungsi publik atau pribadi, ini tentang detail implementasi. Fungsi pribadi hanyalah salah satu aspek dari detail implementasi.
Bagaimanapun, pengujian unit adalah pendekatan pengujian kotak putih. Sebagai contoh, siapa pun yang menggunakan analisis cakupan untuk mengidentifikasi bagian-bagian dari kode yang telah diabaikan dalam pengujian sejauh ini, masuk ke detail implementasi.
A) Ya, Anda harus menguji detail implementasi:
Pikirkan fungsi sortir yang karena alasan kinerja menggunakan implementasi pribadi BubbleSort jika ada hingga 10 elemen, dan implementasi privat dari pendekatan sortir yang berbeda (katakanlah, heapsort) jika ada lebih dari 10 elemen. API publik adalah fungsi sortir. Namun, paket pengujian Anda lebih baik menggunakan pengetahuan bahwa sebenarnya ada dua jenis algoritma yang digunakan.
Dalam contoh ini, tentunya, Anda dapat melakukan tes pada API publik. Ini akan, bagaimanapun, perlu memiliki sejumlah kasus uji yang menjalankan fungsi sortir dengan lebih dari 10 elemen, sehingga algoritma heapsort cukup teruji dengan baik. Keberadaan test case semacam itu saja merupakan indikasi bahwa test suite terhubung dengan detail implementasi fungsi.
Jika detail implementasi perubahan fungsi sortir, mungkin dengan cara batas antara dua algoritma penyortiran digeser atau heapsort digantikan oleh mergesort atau apa pun: Tes yang ada akan terus berfungsi. Nilai mereka bagaimanapun dipertanyakan, dan mereka sepertinya perlu dikerjakan ulang untuk menguji fungsi sortir yang lebih baik. Dengan kata lain, akan ada upaya pemeliharaan terlepas dari kenyataan bahwa tes ada di API publik.
B) Cara menguji detail implementasi
Salah satu alasan mengapa banyak orang berpendapat seseorang tidak boleh menguji fungsi pribadi atau detail implementasi adalah, bahwa detail implementasi lebih cenderung berubah. Kemungkinan perubahan yang lebih tinggi ini setidaknya adalah salah satu alasan untuk menyembunyikan detail implementasi di belakang antarmuka.
Sekarang, asumsikan bahwa implementasi di belakang antarmuka berisi bagian pribadi yang lebih besar di mana tes individu pada antarmuka internal mungkin menjadi pilihan. Beberapa orang berpendapat, bagian-bagian ini tidak boleh diuji ketika pribadi, mereka harus diubah menjadi sesuatu yang umum. Setelah publik, unit-test kode itu akan baik-baik saja.
Ini menarik: Walaupun antarmuka internal, kemungkinan akan berubah, menjadi detail implementasi. Mengambil antarmuka yang sama, menjadikannya publik melakukan beberapa transformasi ajaib, yaitu mengubahnya menjadi antarmuka yang cenderung berubah. Jelas ada beberapa kelemahan dalam argumentasi ini.
Namun, ada beberapa kebenaran di balik ini: Ketika menguji rincian implementasi, khususnya menggunakan antarmuka internal, seseorang harus berusaha untuk menggunakan antarmuka yang cenderung tetap stabil. Namun, apakah beberapa antarmuka cenderung stabil, tidak hanya dapat ditentukan berdasarkan apakah itu publik atau pribadi. Dalam proyek-proyek dari dunia saya telah bekerja selama beberapa waktu, antarmuka publik juga cukup sering berubah, dan banyak antarmuka pribadi tetap tak tersentuh selama berabad-abad.
Namun, merupakan aturan praktis yang baik untuk menggunakan "pintu depan dulu" (lihat http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Tetapi perlu diingat bahwa itu disebut "pintu depan dulu" dan bukan "pintu depan saja".
C) Ringkasan
Tes juga detail implementasi. Pilih pengujian pada antarmuka yang stabil (publik atau pribadi). Jika detail implementasi berubah, tes pada API publik juga perlu direvisi. Mengubah sesuatu yang privat menjadi publik tidak secara ajaib mengubah stabilitasnya.
sumber
Ya, Anda harus menguji metode pribadi, sedapat mungkin. Mengapa? Untuk menghindari ledakan ruang uji kasus negara yang tidak perlu yang akhirnya hanya secara implisit menguji fungsi pribadi yang sama berulang kali pada input yang sama. Mari kita jelaskan mengapa dengan sebuah contoh.
Perhatikan contoh berikut yang sedikit dibuat-buat. Misalkan kita ingin mengekspos fungsi publik yang mengambil 3 bilangan bulat dan mengembalikan true jika dan hanya jika 3 bilangan bulat itu semuanya prima. Kami mungkin menerapkannya seperti ini:
Sekarang, jika kita mengambil pendekatan ketat bahwa hanya fungsi publik yang harus diuji, kita hanya akan diizinkan untuk menguji
allPrime
dan tidakisPrime
atauandAll
.Sebagai tester, kita mungkin akan tertarik dalam lima kemungkinan setiap argumen:
< 0
,= 0
,= 1
,prime > 1
,not prime > 1
. Tetapi untuk lebih teliti, kita juga harus melihat bagaimana setiap kombinasi argumen bermain bersama. Jadi itu5*5*5
= 125 kasus uji yang kami perlukan untuk menguji fungsi ini secara menyeluruh, sesuai dengan intuisi kami.Di sisi lain, jika kami diizinkan untuk menguji fungsi pribadi, kami dapat mencakup sebanyak mungkin tanah dengan lebih sedikit kasus uji. Kami hanya membutuhkan 5 test case untuk menguji
isPrime
ke level yang sama dengan intuisi kami sebelumnya. Dan dengan hipotesis lingkup kecil yang diajukan oleh Daniel Jackson, kita hanya perlu mengujiandAll
fungsi hingga panjang kecil misalnya 3 atau 4. Yang akan menjadi paling banyak 16 tes lagi. Jadi 21 tes total. Alih-alih 125. Tentu saja, kami mungkin ingin menjalankan beberapa tesallPrime
, tetapi kami tidak akan merasa begitu wajib untuk membahas semua 125 kombinasi skenario input yang kami katakan kami peduli. Hanya beberapa jalan bahagia.Contoh yang dibuat-buat, pasti, tapi itu perlu untuk demonstrasi yang jelas. Dan polanya meluas ke perangkat lunak nyata. Fungsi pribadi biasanya merupakan blok bangunan tingkat terendah, dan karenanya sering digabungkan bersama untuk menghasilkan logika tingkat yang lebih tinggi. Berarti pada level yang lebih tinggi, kami memiliki lebih banyak pengulangan dari item level yang lebih rendah karena berbagai kombinasi.
sumber
isPrime
benar-benar independen, jadi menguji setiap kombinasi secara membabi buta adalah tanpa tujuan. Kedua, menandai fungsi murni yang disebutisPrime
pribadi melanggar begitu banyak aturan desain sehingga saya bahkan tidak tahu harus mulai dari mana.isPrime
harus sangat jelas menjadi fungsi publik. Yang sedang berkata, saya mendapatkan apa yang Anda katakan terlepas dari contoh yang sangat buruk ini. Namun itu dibangun dari premis Anda akan ingin untuk melakukan pengujian kombinasi, ketika dalam sistem perangkat lunak nyata ini jarang ide yang baik.Anda juga dapat menjadikan paket metode Anda privat yaitu default dan Anda harus dapat mengujinya kecuali diperlukan privat.
sumber