Haruskah saya menguji metode pribadi atau hanya metode publik? [Tutup]

348

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?

Patrick Desjardins
sumber
"Haruskah aku menguji pembantu pribadi?" Iya. "Haruskah saya menguji pembantu pribadi secara langsung?" Tergantung, umumnya jika Anda dapat mengujinya dengan mudah melalui antarmuka publik, mengapa mengujinya secara langsung? Jika menjadi rumit untuk menguji semua aspek pembantu melalui antarmuka publik, lalu apakah komponen tersebut telah bertahan lebih lama dari keberadaannya sebagai satu kesatuan?
Mihai Danila

Jawaban:

328

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.

pel
sumber
88
Saya tidak setuju. Idealnya, Anda menulis tes cepat sebelum mulai mengkode suatu fungsi. Pikirkan input khas dan apa outputnya. Tulis tes (yang seharusnya tidak lebih dari beberapa detik) dan kode hingga tes benar. Tidak ada alasan untuk meninggalkan gaya kerja itu untuk metode pribadi.
Frank
254
Mengatakan bahwa metode pribadi tidak perlu pengujian sama seperti mengatakan mobil baik-baik saja selama mobil itu baik-baik saja, dan tidak masalah apa yang ada di bawah tenda. Tapi bukankah menyenangkan mengetahui bahwa beberapa kabel di dalam mulai longgar - bahkan jika pengguna tidak memperhatikan apa pun? Tentu, Anda dapat mempublikasikan semuanya, tetapi apa gunanya? Anda akan selalu menginginkan beberapa metode pribadi.
Frank
37
"Metode pribadi adalah detail implementasi yang harus disembunyikan bagi pengguna kelas." tetapi apakah tes benar-benar di sisi yang sama dari antarmuka kelas sebagai pengguna "biasa" (runtime)? ;)
mlvljr
34
Bahaya mengeluarkan apa pun yang ingin Anda uji ke kelas lain adalah Anda bisa berakhir dengan overhead rekayasa produk Anda yang berlebihan dan memiliki sejuta komponen yang dapat digunakan kembali yang tidak pernah dapat digunakan kembali.
occulus
44
Membandingkan sepotong kode dengan mobil itu salah; kode tidak ' memburuk ' dengan waktu, itu abadi . Jika pengujian Anda dari antarmuka publik hanya untuk menentukan bahwa itu ' terlihat oke ' maka pengujian Anda terhadap kode publik tidak cukup. Dalam hal ini pengujian metode pribadi secara terpisah tidak akan membuat tes keseluruhan selesai tidak peduli seberapa keras Anda mencoba Berkonsentrasi pada pengujian menyeluruh kode publik Anda secara keseluruhan, menggunakan pengetahuan cara kerja kode internal untuk membuat skenario yang tepat.
rustyx
293

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.

Dave Sherohman
sumber
72
Masalahnya di sini adalah bahwa "perubahan kode masa depan" itu selalu berarti refactoring pekerjaan dalam beberapa kelas. Ini terjadi begitu sering sehingga tes menulis menciptakan penghalang untuk refactoring.
Outlaw Programmer
40
Juga, jika Anda terus mengubah tes unit Anda maka Anda telah kehilangan semua konsistensi dalam pengujian Anda dan Anda bahkan akan berpotensi membuat bug dalam tes unit itu sendiri.
17 dari 26
6
@ 17 Jika tes dan implementasi dimodifikasi secara serempak (sepertinya, sepertinya memang demikian), akan ada lebih sedikit masalah.
mlvljr
11
@ Sauronlord, Alasan Anda menguji metode pribadi adalah karena jika Anda hanya menguji metode publik, ketika tes gagal kita tidak tahu secara langsung di mana akar penyebab kegagalan tersebut. Itu bisa di salah satu testDoSomething()atau testDoSomethingPrivate(). Ini membuat tes kurang berharga. . Berikut ini lebih banyak alasan untuk menguji stackoverflow.com/questions/34571/… pribadi :
Pacerier
3
@Pacerier Ada juga perbedaan antara pengujian kode Anda, dan memiliki proses pengujian otomatis berkelanjutan. Anda harus memastikan bahwa metode pribadi Anda berfungsi, tetapi Anda tidak boleh melakukan tes yang menghubungkan Anda dengan metode pribadi, karena itu bukan bagian dari kasus penggunaan perangkat lunak.
Didier A.
150

Saya cenderung mengikuti saran Dave Thomas dan Andy Hunt dalam buku mereka Pragmatic Unit Testing :

Secara umum, Anda tidak ingin merusak enkapsulasi apa pun untuk kepentingan pengujian (atau seperti yang sering dikatakan Ibu, "jangan letakkan kemaluan Anda!"). Sebagian besar waktu, Anda harus dapat menguji kelas dengan menggunakan metode publiknya. Jika ada fungsi signifikan yang tersembunyi di balik akses pribadi atau yang dilindungi, itu mungkin merupakan tanda peringatan bahwa ada kelas lain di sana yang berjuang untuk keluar.

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.

Rosellyne Worrall
sumber
9
Saya akan merekomendasikan menonaktifkan unit test yang menargetkan metode pribadi. Mereka adalah kopling kode, dan akan membebani pekerjaan refactoring di masa depan, atau bahkan terkadang menghalangi penambahan atau modifikasi fitur. Adalah baik untuk menulis tes untuk mereka ketika Anda menerapkannya, sebagai cara otomatis bagi Anda untuk menyatakan Anda sedang bekerja, tetapi tidak bermanfaat untuk mempertahankan tes sebagai regresi.
Didier A.
61

Saya agak merasa terdorong untuk menguji fungsi-fungsi pribadi karena saya semakin banyak mengikuti salah satu rekomendasi QA terbaru kami dalam proyek kami:

Tidak lebih dari 10 dalam kompleksitas siklomatik per fungsi.

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.

VONC
sumber
2
untuk bereaksi terhadap @jop, saya tidak merasa perlu untuk mengekspor fungsi-fungsi pribadi tersebut (dibuat karena pembagian fungsi publik kompleks yang terlalu siklomatik) ke kelas lain. Saya suka mereka masih terikat erat dengan fungsi publik, di kelas yang sama. Tapi masih diuji unit.
VonC
2
Pengalaman saya adalah bahwa metode pribadi itu hanya metode utilitas yang digunakan kembali oleh metode publik itu. Kadang-kadang lebih mudah untuk membagi kelas asli menjadi dua (atau tiga) kelas yang lebih kohesif, menjadikan metode privat itu publik di kelas mereka sendiri, dan karenanya dapat diuji.
Lompat
7
tidak, dalam kasus saya, fungsi-fungsi pribadi baru itu benar-benar bagian dari algoritma yang lebih besar yang diwakili oleh fungsi publik. Fungsi itu dibagi dalam bagian-bagian yang lebih kecil, yang bukan utilitas, tetapi langkah-langkah dari proses yang lebih besar. Oleh karena itu perlunya unit-test mereka (daripada unit-test seluruh algo sekaligus)
VonC
Bagi mereka yang tertarik dengan kompleksitas siklomatik, saya menambahkan pertanyaan pada topik: stackoverflow.com/questions/105852/…
VonC
Ups, url pertanyaan berubah karena salah ketik pada judul! stackoverflow.com/questions/105852/…
VonC
51

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

  • Pengujian unit langsung (tidak perlu 'retasan')

Kekurangan

  • Programmer perlu memahami metode publik, sementara dia hanya ingin menguji metode pribadi
  • Anda tidak menguji bagian aplikasi terkecil yang dapat diuji

2. Jika privat sangat penting, maka mungkin ini merupakan kode untuk membuat kelas baru yang terpisah

Keuntungan

  • Anda dapat merevisi ini ke kelas baru, karena jika itu penting, kelas lain mungkin juga membutuhkannya
  • Unit yang dapat diuji sekarang merupakan metode publik, sehingga dapat diuji

Kekurangan

  • Anda tidak ingin membuat kelas jika tidak diperlukan, dan hanya digunakan oleh kelas dari mana metode tersebut berasal
  • Kerugian kinerja potensial karena penambahan overhead

3. Ubah pengubah akses ke (final) yang diproteksi

Keuntungan

  • Anda sedang menguji bagian aplikasi terkecil yang dapat diuji. Saat menggunakan final protected, fungsi tidak akan dapat ditimpa (sama seperti pribadi)
  • Tidak ada kehilangan kinerja
  • Tidak ada overhead tambahan

Kekurangan

  • Anda mengubah akses pribadi menjadi terlindungi, yang berarti dapat diakses oleh anak-anak itu
  • Anda masih membutuhkan kelas Mock di kelas tes untuk menggunakannya

Contoh

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

Jadi unit pengujian kami sekarang dapat memanggil test_sleepWithSuspect untuk menguji fungsi pribadi kami sebelumnya.

eddy147
sumber
eddy147, saya sangat suka konsep pengujian metode yang dilindungi melalui tiruan. Terima kasih!!!!
Theodore R. Smith
15
Saya hanya ingin menunjukkan bahwa dalam deskripsi asli TDD, dalam pengujian unit, unit adalah kelas , bukan metode / fungsi. Jadi ketika Anda menyebutkan "menguji bagian terkecil aplikasi", salah untuk merujuk bagian terkecil yang dapat diuji sebagai metode. Jika Anda menggunakan logika itu, Anda mungkin berbicara satu baris kode daripada seluruh blok kode.
Matt Quigley
@ Matt Satuan kerja dapat menunjuk ke satu kelas, tetapi juga satu metode.
eddy147
4
@ eddy147 Unit testing dilengkapi Test Driven Development, di mana unit didefinisikan sebagai kelas. Seperti yang terjadi dengan The Internets, semantik telah berkembang berarti banyak hal (yaitu bertanya kepada 2 orang apa perbedaan antara pengujian unit dan integrasi, dan Anda akan mendapatkan 7 jawaban). TDD dimaksudkan sebagai cara untuk menulis perangkat lunak dengan prinsip-prinsip SOLID, termasuk Tanggung Jawab Tunggal, di mana kelas memiliki tanggung jawab tunggal dan tidak boleh memiliki kompleksitas siklus yang tinggi. Dalam TDD, Anda menulis kelas dan menguji bersama, keduanya unit. Metode pribadi dienkapsulasi tidak memiliki unit test yang sesuai.
Matt Quigley
"Ketika kita membuat kode, kita menggunakan test-driven-design (TDD). Ini berarti bahwa kadang-kadang kita menemukan fungsionalitas yang bersifat pribadi dan ingin diuji." Saya sangat tidak setuju dengan pernyataan ini, silakan lihat jawaban saya di bawah ini untuk perincian lebih lanjut. TDD tidak berarti Anda dipaksa untuk menguji metode pribadi. Anda dapat memilih untuk menguji metode pribadi: dan itu pilihan Anda, tetapi bukan TDD yang membuat Anda melakukan hal seperti itu.
Matt Messersmith
41

Saya tidak suka menguji fungsionalitas pribadi karena beberapa alasan. Mereka adalah sebagai berikut (ini adalah poin utama untuk orang-orang TLDR):

  1. Biasanya ketika Anda tergoda untuk menguji metode pribadi kelas, itu bau desain.
  2. Anda dapat mengujinya melalui antarmuka publik (yang merupakan cara Anda ingin mengujinya, karena itulah cara klien akan memanggil / menggunakannya). Anda bisa mendapatkan rasa aman yang salah dengan melihat lampu hijau pada semua tes yang lulus untuk metode pribadi Anda. Jauh lebih baik / aman untuk menguji kasus tepi pada fungsi pribadi Anda melalui antarmuka publik Anda.
  3. Anda berisiko mengalami duplikasi tes berat (tes yang terlihat / terasa sangat mirip) dengan menguji metode pribadi. Ini memiliki konsekuensi besar ketika persyaratan berubah, karena lebih banyak tes dari yang diperlukan akan rusak. Itu juga dapat menempatkan Anda pada posisi di mana sulit untuk refactor karena test suite Anda ... yang merupakan ironi utama, karena test suite ada untuk membantu Anda mendesain ulang dan refactor dengan aman!

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.

Penilai Aturan

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):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

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 mengubah RuleEvaluatorkelas 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 membuat Tokenizerkelas, maka metode publiknya adalah HasMoreTokens()danGetNextTokens() . The RuleEvaluatorkelas bisa memilikiTokenizerobjek sebagai anggota. Sekarang, kita dapat menyimpan tes yang sama seperti di atas, kecuali kita menguji Tokenizerkelas, bukan RuleEvaluatorkelas.

Berikut ini tampilannya di UML:

Evaluator Peraturan Refactored

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 di Tokenizerkelas:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

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 menggunakanFRIEND_TESTatau 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 seperti FRIEND_TESTdan / atau refleksi, Anda biasanya akan menyesalinya dalam jangka panjang.

Pertimbangkan kemungkinan implementasi Tokenizerkelas berikut:

masukkan deskripsi gambar di sini

Katakanlah yang SplitUpByDelimiter()bertanggung jawab untuk mengembalikan array sehingga setiap elemen dalam array adalah token. Lebih jauh, katakan saja itu GetNextToken()hanyalah iterator pada vektor ini. Jadi tes publik Anda mungkin terlihat seperti ini:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

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_TESTdari googletest, atau refleksi jika bahasa mendukungnya.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Nah, sekarang katakanlah persyaratan berubah, dan tokenizing menjadi jauh lebih kompleks. Anda memutuskan bahwa pembatas string sederhana tidak akan cukup, dan Anda membutuhkan Delimiterkelas 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:

  1. 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.

  2. 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

Matt Messersmith
sumber
Masalah yang saya miliki dengan mendaftar "Anda dapat berpotensi menggunakan kembali kelas-kelas ini di bagian lain dari sistem Anda" sebagai keuntungan, adalah bahwa kadang-kadang alasan saya menandai suatu fungsi adalah pribadi adalah karena saya tidak ingin itu digunakan oleh bagian lain dari sistem. Ini adalah masalah khusus bahasa: idealnya, ini bersifat pribadi untuk "modul", tetapi jika bahasa tidak mendukung itu (misalnya PHP), kelas saya mewakili modul, bukan unit: metode pribadi adalah kode yang dapat digunakan kembali dengan kontrak mereka sendiri, tetapi hanya harus digunakan kembali dalam kelas itu.
IMSoP
Saya mengerti apa yang Anda katakan, tapi saya suka cara komunitas Python menangani masalah itu. Jika Anda memberi nama anggota "pribadi" yang dipertanyakan dengan pemimpin _, 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 umum
Matt Messersmith
26

Saya 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.

17 dari 26
sumber
21

Jika metode pribadi Anda tidak diuji dengan memanggil metode publik Anda, lalu apa yang dilakukannya? Saya berbicara pribadi tidak dilindungi atau teman.

chrissie1
sumber
3
Terima kasih. Ini adalah komentar yang sangat diremehkan dan terutama masih relevan, bahkan setelah hampir 8 tahun sejak ditulis.
Sauronlord
1
Dengan alasan yang sama, orang hanya bisa berargumen untuk menguji perangkat lunak dari antarmuka pengguna (pengujian tingkat sistem), karena bagaimanapun setiap fungsi dalam perangkat lunak akan dieksekusi entah bagaimana dari sana.
Dirk Herrmann
18

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

Adam Davis
sumber
1
Dalam hal ini, tidakkah masuk akal untuk memindahkan metode privat itu ke kelas lain, lalu membuatnya menjadi publik atau statis publik?
Outlaw Programmer
+1 Jika Anda tidak menguji fungsi anggota pribadi Anda dan pengujian antarmuka publik Anda gagal semua yang Anda dapatkan adalah hasil yang setara dengan sesuatu yang rusak tanpa tahu apa itu sesuatu.
Olumide
12

Jika Anda mengembangkan test driven (TDD), Anda akan menguji metode pribadi Anda.

Jader Dias
sumber
2
Anda akan mengekstrak metode pribadi setelah refactoring agiletips.blogspot.com/2008/11/...
Josh Johnson
4
Tidak benar, Anda menguji metode publik Anda dan setelah tes lulus maka Anda mengekstrak kode dalam metode publik Anda menjadi metode pribadi selama langkah "pembersihan". Menguji metode pribadi adalah ide yang buruk karena membuat mengubah implementasi jauh lebih sulit (bagaimana jika suatu hari Anda ingin mengubah cara Anda melakukan sesuatu, Anda harus dapat mengubahnya dan menjalankan semua tes Anda dan jika cara baru Anda melakukan hal itu). hal yang benar mereka harus lulus, saya tidak ingin harus mengubah semua tes pribadi saya untuk ini).
Tesseract
1
@Tesseract, jika saya bisa membenarkan komentar Anda lebih dari sekali saya akan. "... Anda harus dapat mengubahnya dan menjalankan semua tes Anda dan jika cara baru Anda melakukan hal itu benar mereka harus lulus" ITULAH adalah salah satu manfaat utama dari tes unit. Mereka memungkinkan Anda untuk refactor dengan percaya diri. Anda dapat sepenuhnya mengubah pekerjaan pribadi kelas Anda dan (tanpa menulis ulang semua tes unit Anda) yakin bahwa Anda tidak merusak apa pun karena semua tes unit Anda (yang ada) (pada antarmuka publik Anda) masih berlalu.
Lee
Tidak setuju, lihat jawaban saya di bawah ini
Matt Messersmith
11

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.

maxbog
sumber
Di mana implementasi kemudian diuji? Jika beberapa fungsi menggunakan caching, apakah ini kemudian detail implementasi dan caching tidak diuji?
Dirk Herrmann
11

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.

Tom Carr
sumber
9

Tes unit saya percaya adalah untuk menguji metode publik. Metode publik Anda menggunakan metode pribadi Anda, sehingga secara tidak langsung mereka juga diuji.

scubabbl
sumber
7

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.

  1. Menguji metode pribadi, TDD dan Refactoring Berbasis Tes
  2. Pengembangan Test-Driven Bukan Pengujian

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 .

dkinzer
sumber
6

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.

Adron
sumber
2
Saya tidak setuju. "Jika kamu tidak menguji metode pribadi kamu, bagaimana kamu tahu mereka tidak akan merusak?" : Saya tahu ini karena jika metode pribadi saya rusak, maka tes yang menguji metode publik saya yang mengandalkan metode pribadi itu akan gagal. Saya tidak ingin harus mengubah tes saya setiap kali saya berubah pikiran tentang cara menerapkan metode publik. Saya juga berpikir bahwa kepentingan utama unit test bukanlah untuk mengetahui secara spesifik baris kode mana yang salah, melainkan memungkinkan Anda untuk lebih atau kurang percaya diri bahwa Anda tidak merusak apa pun ketika melakukan perubahan (pada metode pribadi).
Tesseract
6

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.

fernandezdavid7
sumber
3

Jika Anda tidak menguji metode pribadi Anda, bagaimana Anda tahu mereka tidak akan rusak?

Billy Jo
sumber
19
Dengan menulis melalui pengujian metode publik Anda.
scubabbl
3
Metode privat itu konon disebut dengan metode publik kelas. Jadi coba saja metode publik yang memanggil metode privat.
Lompat
1
Jika metode publik Anda berfungsi dengan baik maka jelas metode pribadi yang mereka akses berfungsi dengan benar.
17 dari 26
Jika pengujian metode publik Anda gagal, Anda langsung tahu bahwa ada sesuatu yang tidak benar pada tingkat yang lebih rendah di objek / komponen / dll
Rob
3
Akan sangat menyenangkan mengetahui bahwa ini adalah fungsi internal dan bukan hanya fungsi eksternal yang rusak (atau sebaliknya fungsi internal baik-baik saja dan Anda dapat fokus pada eksternal).
Adam Davis
2

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.

dvorak
sumber
5
Kata kunci teman membuat saya sedih.
Rob
Ini bukan masalah jika kelas pengujian diimplementasikan di proyek lain. Yang penting adalah bahwa kode produksi tidak merujuk pada kelas pengujian.
Olumide
2

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:

  • Apakah metode ini dipanggil lebih dari sekali dari tempat yang berbeda?
  • Apakah metode ini cukup canggih untuk memerlukan tes?
Olivier Pichon
sumber
1

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.

Andy
sumber
1

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.

magallanes
sumber
1

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.

tkruse
sumber
0

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
1
@VisibleForTesting adalah anotasi untuk itu. Saya tidak akan bersantai enkapsulasi untuk pengujian, melainkan menggunakan dp4j.com
simpatico
0

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".

aemorales1
sumber
Tren saat ini adalah untuk mengurangi atau tidak memiliki tim QA. Tes unit ini menjadi tes otomatis yang dijalankan setiap kali seorang insinyur mendorong kode pada cabang master. Bahkan dengan QA, tidak ada cara mereka dapat menguji seluruh aplikasi secepat tes otomatis.
Patrick Desjardins
0

Jawaban untuk "Haruskah saya menguji metode pribadi?" adalah "....... terkadang". Biasanya Anda harus menguji terhadap antarmuka kelas Anda.

  • Salah satu alasannya adalah karena Anda tidak perlu cakupan ganda untuk fitur.
  • Alasan lain adalah bahwa jika Anda mengubah metode pribadi, Anda harus memperbarui setiap tes untuk mereka, bahkan jika antarmuka objek Anda tidak berubah sama sekali.

Berikut ini sebuah contoh:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

Dalam RefactoredThingAnda 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:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

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.

unflores
sumber
0

Tidak. Anda tidak boleh menguji Metode Pribadi mengapa? dan apalagi kerangka kerja mengejek yang populer seperti Mockito tidak memberikan dukungan untuk pengujian metode pribadi.

cammando
sumber
0

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.

Supun Wijerathne
sumber
0

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.

Dirk Herrmann
sumber
0

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:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Sekarang, jika kita mengambil pendekatan ketat bahwa hanya fungsi publik yang harus diuji, kita hanya akan diizinkan untuk menguji allPrimedan tidak isPrimeatau andAll.

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 itu 5*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 isPrimeke level yang sama dengan intuisi kami sebelumnya. Dan dengan hipotesis lingkup kecil yang diajukan oleh Daniel Jackson, kita hanya perlu menguji andAllfungsi 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.

Colm Bhandal
sumber
Pertama, Anda tidak perlu menguji kombinasi seperti itu dengan fungsi murni seperti yang Anda tunjukkan. Panggilan untuk isPrimebenar-benar independen, jadi menguji setiap kombinasi secara membabi buta adalah tanpa tujuan. Kedua, menandai fungsi murni yang disebut isPrimepribadi melanggar begitu banyak aturan desain sehingga saya bahkan tidak tahu harus mulai dari mana. isPrimeharus 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.
Matt Messersmith
Matt ya contohnya tidak ideal saya akan memberi Anda itu. Tetapi prinsipnya harus jelas.
Colm Bhandal
Premisnya tidak persis seperti yang Anda inginkan untuk melakukan pengujian kombinasi. Itu yang harus Anda lakukan, jika Anda membatasi diri untuk menguji hanya potongan-potongan teka-teki publik. Ada kasus di mana Anda ingin membuat fungsi murni privat untuk mematuhi prinsip enkapsulasi yang tepat. Dan fungsi pribadi murni ini dapat digunakan oleh yang publik. Dengan cara kombinasi, dengan fungsi pribadi murni lainnya mungkin. Dalam hal ini, mengikuti dogma yang tidak akan Anda tes pribadi, Anda akan dipaksa untuk melakukan pengujian kombinasi pada fungsi publik daripada melakukan pengujian modular komponen pribadi.
Colm Bhandal
0

Anda juga dapat menjadikan paket metode Anda privat yaitu default dan Anda harus dapat mengujinya kecuali diperlukan privat.

Yogesh
sumber