Mengapa unit menguji metode pribadi dianggap sebagai praktik buruk?

16

Konteks:

Saat ini saya sedang mengerjakan proyek kecil dengan Python. Saya biasanya menyusun kelas saya dengan beberapa metode publik yang didokumentasikan tetapi terutama berkaitan dengan konsep tingkat tinggi (apa yang harus diketahui dan digunakan oleh pengguna kelas), dan banyak metode tersembunyi (dimulai dengan garis bawah) yang bertanggung jawab atas pemrosesan tingkat kompleks atau rendah.

Saya tahu bahwa tes sangat penting untuk memberikan kepercayaan pada kode dan untuk memastikan bahwa modifikasi selanjutnya tidak merusak perilaku sebelumnya.

Masalah:

Untuk membangun metode publik tingkat tinggi di atas dasar tepercaya, saya biasanya menguji metode pribadi. Saya merasa lebih mudah untuk menemukan apakah modifikasi kode telah memperkenalkan regresi dan di mana. Ini berarti bahwa tes internal tersebut dapat melanggar revisi kecil dan perlu diperbaiki / diganti

Tetapi saya juga tahu bahwa pengujian unit metode pribadi setidaknya merupakan konsep yang disengketakan atau lebih sering dianggap sebagai praktik yang buruk. Alasannya adalah: hanya perilaku publik yang harus diuji ( ref. )

Pertanyaan:

Saya peduli mengikuti praktik terbaik dan ingin memahami:

  • mengapa menggunakan tes unit pada metode pribadi / tersembunyi buruk (apa risikonya)?
  • apa praktik terbaik ketika metode publik dapat menggunakan pemrosesan tingkat rendah dan / atau kompleks?

Presisi:

  • ini bukan pertanyaan bagaimana . Python tidak memiliki konsep privasi dan metode tersembunyi yang tidak didaftar tetapi dapat digunakan saat Anda mengetahui namanya
  • Saya tidak pernah diajari aturan dan pola pemrograman: kelas terakhir saya berasal dari tahun 80-an ... Saya terutama belajar bahasa melalui percobaan dan kegagalan dan referensi di Internet (Stack Exchange menjadi favorit saya selama bertahun-tahun)
Serge Ballesta
sumber
2
Kemungkinan duplikat dari Metode pribadi yang dilindungi
Greg Burghardt
2
OP, di mana Anda mendengar atau membaca bahwa pengujian metode pribadi dianggap "buruk"? Ada berbagai cara untuk menguji unit. Lihat Pengujian unit, Pengujian kotak hitam, dan Pengujian kotak putih .
John Wu
@ JohnWu: Saya tahu perbedaan antara pengujian White-box dan Black-box. Tetapi bahkan dalam pengujian White-box sepertinya kebutuhan untuk menguji metode pribadi adalah petunjuk untuk masalah desain. Pertanyaan saya adalah upaya untuk memahami apa jalan terbaik ketika saya jatuh di sana ...
Serge Ballesta
2
Sekali lagi, di mana Anda mendengar atau membaca bahwa bahkan dalam pengujian White-box kebutuhan untuk menguji metode pribadi adalah petunjuk untuk masalah desain? Saya ingin memahami alasan di balik kepercayaan itu sebelum mencoba jawaban.
John Wu
@SergeBallesta dengan kata lain, berikan beberapa referensi ke artikel-artikel yang membuat Anda percaya bahwa menguji metode pribadi adalah praktik yang buruk. Kemudian jelaskan kepada kami mengapa Anda memercayai mereka.
Laiv

Jawaban:

17

Beberapa alasan:

  1. Biasanya ketika Anda tergoda untuk menguji metode pribadi kelas, itu bau desain (kelas gunung es, komponen publik tidak cukup dapat digunakan kembali, dll). Hampir selalu ada masalah "lebih besar" yang sedang dimainkan.

  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!

Kiat jika Anda masih tergoda untuk menguji bagian-bagian pribadi (jangan gunakan jika itu mengganggu Anda, dan YMMV, tetapi itu telah bekerja dengan baik untuk saya di masa lalu): Kadang-kadang menulis unit test untuk fungsi pribadi hanya untuk memastikan mereka bekerja persis bagaimana menurut Anda mereka bisa berharga (terutama jika Anda baru mengenal suatu bahasa). Namun, setelah Anda yakin mereka bekerja, hapus tes, dan selalu pastikan bahwa tes yang menghadap publik solid dan akan menangkap jika seseorang membuat perubahan yang mengerikan untuk fungsi pribadi tersebut.

Kapan menguji metode pribadi: Karena jawaban ini sudah (agak) populer, saya merasa berkewajiban untuk menyebutkan bahwa "praktik terbaik" selalu hanya itu: "praktik terbaik". Itu tidak berarti Anda harus melakukannya secara dogmatis atau buta. Jika Anda pikir Anda harus menguji metode pribadi Anda dan memiliki alasan yang sah (seperti Anda sedang menulis tes karakterisasi untuk aplikasi warisan), maka uji metode pribadi Anda . Keadaan khusus selalu mengalahkan aturan umum atau praktik terbaik. Sadarilah beberapa hal yang bisa salah (lihat di atas).

Saya punya jawaban yang membahas hal ini secara rinci pada SO yang tidak akan saya ulangi di sini: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015

Matt Messersmith
sumber
4
Alasan 1: Nebula. Alasan 2: Bagaimana jika metode pembantu pribadi Anda tidak boleh menjadi bagian dari API publik? Alasan 3: Tidak jika Anda mendesain kelas Anda dengan benar. Kiat terakhir Anda: mengapa saya menghapus tes yang sangat bagus yang membuktikan bahwa metode yang saya tulis berhasil?
Robert Harvey
4
@RobertHarvey Alasan 2: dapat diakses secara tidak langsung melalui API publik! = Menjadi bagian dari API publik. Jika fungsi pribadi Anda tidak dapat diuji melalui API publik, maka mungkin itu adalah kode mati dan harus dihapus? Atau kelas Anda memang gunung es (alasan 1) dan harus di refactored.
Frax
5
@RobertHarvey jika Anda tidak dapat menguji fungsi pribadi melalui API publik kemudian hapus karena tidak ada gunanya.
David Arno
1
@RobertHarvey 1: Bau desain selalu agak subyektif / samar-samar, jadi pasti. Tetapi saya telah mendaftar beberapa contoh nyata dari anti-pola, dan ada lebih banyak detail dalam jawaban SO saya. 2: Metode pribadi tidak dapat menjadi bagian dari API publik (menurut definisi: mereka pribadi) ... jadi saya rasa pertanyaan Anda tidak masuk akal. Saya mencoba untuk sampai pada titik bahwa jika Anda memiliki sesuatu seperti bin_search(arr, item)(publik) dan bin_search(arr, item, low, hi)(pribadi, ada banyak cara untuk melakukan pencarian bin), maka yang perlu Anda uji adalah yang menghadap publik ( bin_search(arr, item))
Matt Messersmith
1
@RobertHarvey 3: Pertama, saya katakan risiko , bukan jaminan . Kedua, mengklaim "itu berhasil jika Anda melakukannya dengan benar" memuaskan diri sendiri. Misalnya, "Anda dapat menulis sistem operasi dalam satu fungsi jika Anda melakukannya dengan benar ". Ini tidak salah: tetapi juga tidak berguna. Tentang kiat: Anda akan menghapusnya karena jika implementasi Anda berubah (yaitu Anda ingin menukar keluar alat pribadi), maka rangkaian uji Anda akan menghalangi Anda (Anda akan memiliki tes gagal di tempat yang seharusnya tidak Anda lakukan).
Matt Messersmith
13

Mengingat bahwa salah satu tujuan utama pengujian unit adalah Anda dapat memperbaiki internal program Anda dan kemudian dapat memverifikasi bahwa Anda belum merusak fungsinya, itu kontraproduktif jika pengujian unit Anda beroperasi pada tingkat granularitas yang sangat halus sehingga setiap perubahan pada kode program mengharuskan Anda menulis ulang tes Anda.

Pete
sumber
Tidak yakin mengapa jawaban Anda telah dibatalkan. Singkat, to the point dan mendapat jawaban 100% benar.
David Arno
3
@ Davidvido: Mungkin karena pengujian metode pribadi tidak benar-benar ada hubungannya dengan pengujian granularity. Ini semua terkait dengan penggabungan ke detail implementasi.
Robert Harvey
10

Tes unit menulis untuk metode pribadi mengikat tes unit Anda dengan detail implementasi.

Tes unit harus menguji perilaku kelas di permukaan luar kelas (itu API publik). Tes unit seharusnya tidak perlu tahu apa-apa tentang jeroan kelas. Tes unit tulisan terhadap detail implementasi kelas mengikat tangan Anda ketika tiba saatnya untuk melakukan refactor. Refactoring hampir pasti akan memecahkan tes-tes itu, karena mereka bukan bagian dari API stabil Anda.

Yang mengatakan, mengapa mungkin Anda ingin menulis unit test untuk metode pribadi Anda?

Ada ketegangan alami antara tes unit dan pengembangan bertahap. Pengembang perangkat lunak yang menggunakan REPL (read-eval-print loop) dapat membuktikan seberapa produktifnya dengan cepat menulis dan menguji sedikit fungsionalitas saat Anda "menumbuhkan" kelas atau fungsi. Satu-satunya cara yang baik untuk melakukan itu di lingkungan yang didorong unit test adalah menulis unit test untuk metode pribadi, tetapi ada banyak gesekan dalam melakukan itu. Tes unit membutuhkan waktu untuk menulis, Anda memerlukan metode yang sebenarnya untuk diuji, dan kerangka kerja pengujian Anda perlu mendukung kemampuan untuk menjaga agar metode ini tetap pribadi sehingga tidak mencemari API eksternal Anda.

Beberapa ekosistem seperti C # dan .NET memiliki cara untuk membuat lingkungan seperti REPL (alat seperti Linqpad melakukan ini), tetapi utilitas mereka terbatas karena Anda tidak memiliki akses ke proyek Anda. Jendela langsung di Visual Studio tidak nyaman; masih tidak memiliki Intellisense penuh, Anda harus menggunakan nama yang sepenuhnya memenuhi syarat di dalamnya, dan memicu pembangunan setiap kali Anda menggunakannya.

Robert Harvey
sumber
4
@ user949300 Agak sulit untuk memperdebatkan ini tanpa jatuh ke dalam kekeliruan Skotlandia yang tidak benar , tetapi ada banyak tes yang ditulis dengan buruk dari semua jenis. Dari perspektif pengujian unit, Anda harus menguji kontrak publik dari metode Anda tanpa mengetahui detail implementasi internal. Bukan berarti menyatakan bahwa ketergantungan tertentu telah disebut X kali selalu salah: ada situasi di mana ini masuk akal. Anda hanya perlu memastikan bahwa ini adalah informasi yang sebenarnya ingin Anda sampaikan dalam kontrak unit yang sedang diuji.
Vincent Savard
3
@ DavidVrno: [mengangkat bahu] Saya sudah melakukan ini untuk sementara waktu sekarang. Tes unit untuk metode pribadi selalu bekerja dengan baik untuk saya, sampai Microsoft memutuskan untuk berhenti mendukung objek proxy dalam kerangka pengujian mereka. Akibatnya tidak ada yang meledak. Saya tidak pernah merobek lubang di alam semesta dengan menulis tes untuk metode pribadi.
Robert Harvey
2
@ Davidvido: Mengapa saya berhenti menggunakan teknik yang sangat bagus yang memberi saya manfaat, hanya karena seseorang di internet mengatakan itu ide yang buruk tanpa memberikan pembenaran?
Robert Harvey
2
Manfaat utama yang saya dapatkan dari unit test adalah memberi saya "jaring pengaman" yang memungkinkan saya untuk mengotak-atik kode saya, dan yakin mengetahui perubahan saya tidak memperkenalkan regresi. Untuk itu, menguji metode pembantu pribadi membuatnya lebih mudah untuk menemukan regresi semacam itu. Ketika saya refactor metode pembantu pribadi dan memperkenalkan kesalahan logika, saya istirahat tes khusus untuk metode pribadi itu. Jika pengujian unit saya lebih umum dan hanya menguji antarmuka unit kode itu, maka masalahnya akan jauh lebih tidak jelas untuk ditemukan.
Alexander - Reinstate Monica
2
@Frax Tentu, saya bisa, tetapi dengan logika itu, saya harus melupakan tes unit yang mendukung tes integrasi sistem. Lagi pula, "dalam kebanyakan kasus Anda harus dapat memodifikasi tes ini untuk menguji perilaku yang sama"
Alexander - Reinstate Monica
6

Dari pengalaman saya, saya telah menemukan bahwa unit menguji kelas internal, metode biasanya berarti bahwa saya harus mengambil fungsi yang diuji, keluar kelas. Untuk membuat level abstraksi lain.

Ini mengarah pada kepatuhan yang lebih baik terhadap Prinsip Tanggung Jawab Tunggal.

Robert Andrzantai
sumber
Ini harus menjadi jawaban yang diterima.
Jack Aidley
0

Saya pikir ini adalah pertanyaan yang bagus karena memaparkan masalah umum dalam menguji cakupan. Tetapi jawaban yang baik harus memberi tahu Anda bahwa pertanyaannya tidak tepat karena, secara teori, Anda seharusnya tidak dapat menguji metode pribadi . Itu sebabnya mereka pribadi .

Mungkin pertanyaan yang lebih baik adalah "Apa yang harus saya lakukan ketika saya ingin menguji metode pribadi?" , dan jawabannya agak jelas: Anda harus mengeksposnya dengan cara yang memungkinkan pengujian. Sekarang, ini tidak selalu berarti bahwa Anda hanya perlu membuat metode publik dan hanya itu. Kemungkinan besar Anda ingin melakukan abstraksi yang lebih tinggi; pindah ke pustaka atau API yang berbeda sehingga Anda dapat melakukan tes di pustaka itu, tanpa memaparkan fungsi itu di API utama Anda.

Ingatlah bahwa ada alasan mengapa metode Anda memiliki tingkat aksesibilitas yang berbeda, dan Anda harus selalu memikirkan bagaimana kelas Anda akan digunakan pada akhirnya.

Juan Carlos Eduardo Romaina Ac
sumber
Saya mencoba menjawab ini dalam pertanyaan saya dengan mengatakan bahwa Python tidak memiliki konsep privasi dan metode tersembunyi yang benar-benar tidak terdaftar tetapi dapat digunakan ketika Anda tahu nama mereka
Serge Ballesta