Terkadang fungsi pribadi hanyalah unit fungsional internal yang belum diekstraksi. Jadi mengapa tidak mengujinya?

9

Kadang-kadang fungsi pribadi dari suatu modul atau kelas hanyalah unit fungsional internal internal yang belum diekstraksi, yang mungkin pantas untuk diuji sendiri. Jadi mengapa tidak mengujinya? Kami akan menulis tes untuk mereka nanti jika / ketika mereka diekstraksi. Jadi mengapa tidak menulis tes sekarang, ketika mereka masih bagian dari file yang sama?

Untuk menunjukkan:

masukkan deskripsi gambar di sini

Pertama, saya menulis module_a. Sekarang saya ingin menulis tes untuk itu. Saya ingin menguji fungsi 'pribadi' _private_func. Saya tidak mengerti mengapa saya tidak akan menulis tes untuk itu, jika nanti saya mungkin refactor ke modul internalnya sendiri, dan kemudian menulis tes untuk itu.


Misalkan saya memiliki modul dengan fungsi-fungsi berikut (bisa juga kelas):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuffdan _do_more_stuffmerupakan fungsi 'pribadi' dari modul.

Saya memahami gagasan bahwa kita hanya harus menguji antarmuka publik, bukan detail implementasi. Namun, ada satu hal:

_do_stuffdan _do_more_stuffberisi sebagian besar fungsi modul. Masing-masing dari mereka bisa menjadi fungsi publik dari modul 'internal' yang berbeda. Tetapi mereka belum berevolusi dan cukup besar untuk diekstraksi ke file yang terpisah.

Jadi menguji fungsi-fungsi ini terasa benar karena mereka adalah unit fungsionalitas yang penting. Jika mereka berada di modul yang berbeda sebagai fungsi publik, kami akan mengujinya. Jadi mengapa tidak menguji mereka ketika mereka belum (atau pernah) diekstraksi ke file yang berbeda?

Aviv Cohn
sumber
2
"Metode privat bermanfaat untuk pengujian unit ..." "... tetap menggunakan metode privat memberi saya peningkatan yang berguna, andal dalam pengujian unit. Sebaliknya, melemahnya batasan akses" untuk testabilitas "hanya memberi saya sesuatu yang kabur, sulit dipahami sepotong kode uji, yang juga beresiko permanen rusak oleh refactoring minor; terus terang apa yang saya dapatkan tampak mencurigakan seperti utang teknis "
Agas
3
"Haruskah aku menguji fungsi pribadi?" Tidak. Tidak pernah, selamanya, selamanya.
David Arno
2
@DavidArno Mengapa? Apa alternatif untuk menguji internal? Hanya tes integrasi? Atau mempublikasikan lebih banyak hal? (meskipun dalam pengalaman saya, saya kebanyakan menguji metode publik di kelas internal, bukan metode pribadi)
CodesInChaos
1
Jika cukup penting bahwa ada kebutuhan untuk menulis tes untuk itu, maka itu harus sudah ada dalam modul terpisah. Jika tidak, maka Anda menguji perilakunya dengan menggunakan API publik.
Vincent Savard

Jawaban:

14

Kebutuhan untuk menguji tidak sama dengan kebutuhan untuk umum.

Kode non trivial membutuhkan pengujian terlepas dari paparan. Perilaku non publik tidak perlu ada apalagi diuji.

Pandangan yang bertentangan ini dapat membuat Anda ingin membuat setiap fungsi menjadi publik atau menolak untuk memfaktorkan kode ke dalam suatu fungsi kecuali itu akan menjadi publik.

Ini bukan jawabannya. Bersedia membuat fungsi pembantu pribadi. Uji mereka melalui antarmuka publik yang menggunakannya sebanyak mungkin.

Jika pengujian melalui antarmuka publik tidak menggunakan fungsi privat, fungsi privat akan mencoba untuk memperbolehkannya.

Validasi dapat membantu mempersempit fungsi privat yang dimungkinkan. Jika Anda tidak bisa melewati nol melalui antarmuka publik, Anda masih bisa melempar pengecualian jika tetap ada.

Kenapa harus kamu? Mengapa menguji apa yang tidak akan pernah Anda lihat? Karena semuanya berubah. Mungkin bersifat pribadi sekarang, tetapi bersifat publik nanti. Kode panggilan bisa berubah. Kode yang secara eksplisit menolak null membuat penggunaan yang tepat dan status yang diharapkan menjadi jelas.

Tentu saja nol bisa baik-baik saja. Ini hanya contoh di sini. Tetapi jika Anda mengharapkan sesuatu, itu berguna untuk membuat harapan itu jelas.

Itu mungkin bukan jenis pengujian yang ada dalam pikiran Anda, tetapi mudah-mudahan Anda akan bersedia untuk membuat fungsi pembantu pribadi saat yang tepat.

Keinginan untuk menguji itu baik, tetapi itu tidak seharusnya menjadi kekuatan pendorong dalam desain API publik Anda. Rancang API publik agar mudah digunakan. Kemungkinan tidak akan terjadi jika setiap fungsi bersifat publik. API harus menjadi sesuatu yang orang bisa mengerti cara menggunakan tanpa masuk ke dalam kode. Jangan biarkan orang-orang seperti itu bertanya-tanya untuk apa fungsi pembantu aneh ini.

Menyembunyikan fungsi pembantu publik dalam modul internal adalah upaya untuk menghormati kebutuhan akan API yang bersih sambil mengekspos para pembantu untuk pengujian. Saya tidak akan mengatakan ini salah. Anda mungkin mengambil langkah pertama menuju lapisan arsitektur yang berbeda. Tapi tolong, kuasai seni menguji fungsi pembantu pribadi melalui fungsi publik yang menggunakannya terlebih dahulu. Dengan begitu Anda tidak akan terlalu menggunakan solusi ini.

candied_orange
sumber
Saya datang dengan pendekatan, saya ingin mendengar pendapat Anda: setiap kali saya berada dalam situasi di mana saya ingin menguji fungsi pribadi, saya akan memeriksa apakah saya dapat mengujinya secara memadai melalui salah satu fungsi publik (termasuk semua kasing tepi, dll). Jika saya bisa, saya tidak akan menulis tes untuk fungsi ini secara khusus, tetapi hanya mengujinya melalui pengujian fungsi publik yang menggunakannya. Namun jika saya merasa fungsinya tidak dapat diuji dengan cukup melalui antarmuka publik dan memang layak untuk diuji sendiri, saya akan mengekstraknya ke modul internal di mana publik, dan menulis tes untuk itu. Bagaimana menurut anda?
Aviv Cohn
Saya akan mengatakan saya menanyakan hal yang sama kepada orang-orang lain yang menjawab di sini :) Saya ingin mendengar pendapat semua orang.
Aviv Cohn
Sekali lagi, saya tidak akan memberi tahu Anda tidak. Saya khawatir Anda tidak mengatakan apa-apa tentang bagaimana hal itu memengaruhi kegunaan. Perbedaan antara publik dan pribadi tidak struktural. Ini penggunaan. Jika perbedaan antara publik dan pribadi adalah pintu depan dan pintu belakang maka pekerjaan Anda adalah membangun gudang di halaman belakang. Baik. Selama orang tidak tersesat di sana.
candied_orange
1
Terpilih untuk "Jika pengujian melalui antarmuka publik tidak menjalankan fungsi pribadi secukupnya, fungsi pribadi mencoba untuk mengizinkan banyak."
Kris Welsh
7

Jawaban singkat: Tidak

Jawaban yang lebih panjang: Ya, tetapi melalui 'API' publik kelas Anda

Seluruh gagasan anggota pribadi suatu kelas adalah bahwa mereka mewakili fungsionalitas yang seharusnya tidak terlihat di luar 'unit' kode, betapapun besar Anda ingin mendefinisikan unit itu menjadi. Dalam kode berorientasi objek bahwa unit sering berakhir menjadi kelas.

Anda harus membuat kelas Anda dirancang sedemikian rupa sehingga dimungkinkan untuk memanggil semua fungsi pribadi melalui berbagai kombinasi keadaan input. Jika Anda menemukan tidak ada cara yang relatif lurus ke depan untuk melakukan ini, itu mungkin mengisyaratkan bahwa desain Anda perlu perhatian lebih dekat.


Setelah klarifikasi pertanyaan, ini hanya soal semantik. Jika kode tersebut dapat beroperasi sebagai unit mandiri yang terpisah, dan sedang diuji seolah-olah itu adalah kode publik, saya tidak dapat melihat manfaat dari tidak memindahkannya ke modul mandiri. Saat ini, ini hanya membingungkan para pengembang di masa depan (termasuk diri Anda, dalam waktu 6 bulan), mengapa kode publik yang tampak tersembunyi di dalam modul lain.

richzilla
sumber
Hai, terima kasih atas jawaban Anda :) Harap baca kembali pertanyaannya, saya telah mengedit untuk menjelaskan.
Aviv Cohn
Saya datang dengan pendekatan, saya ingin mendengar pendapat Anda: setiap kali saya berada dalam situasi di mana saya ingin menguji fungsi pribadi, saya akan memeriksa apakah saya dapat mengujinya secara memadai melalui salah satu fungsi publik (termasuk semua kasing tepi, dll). Jika saya bisa, saya tidak akan menulis tes untuk fungsi ini secara khusus, tetapi hanya mengujinya melalui pengujian fungsi publik yang menggunakannya. Namun jika saya merasa fungsinya tidak dapat diuji dengan cukup melalui antarmuka publik dan memang layak untuk diuji sendiri, saya akan mengekstraknya ke modul internal di mana publik, dan menulis tes untuk itu. Bagaimana menurut anda?
Aviv Cohn
Saya akan mengatakan saya menanyakan hal yang sama kepada orang-orang lain yang menjawab di sini :) Saya ingin mendengar pendapat semua orang.
Aviv Cohn
5

Inti dari fungsi pribadi adalah bahwa mereka adalah detail implementasi tersembunyi yang dapat diubah sesuka hati, tanpa mengubah API publik. Untuk kode contoh Anda:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

Jika Anda memiliki serangkaian tes yang hanya digunakan public_func, maka jika Anda menulis ulang untuk:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

kemudian, selama hasil pengembalian untuk nilai tertentu atetap sama, maka semua tes Anda akan baik. Jika hasil pengembalian berubah, tes akan gagal, menyoroti fakta bahwa ada sesuatu yang rusak.

Ini semua adalah hal yang baik: API publik statis; pekerjaan batin yang dikemas dengan baik; dan tes yang kuat.

Namun, jika Anda akan menulis tes untuk _do_stuffatau _do_more_stuffkemudian membuat perubahan di atas, Anda sekarang akan memiliki banyak tes yang rusak, bukan karena fungsi berubah, tetapi karena implementasi fungsi itu berubah. Tes-tes itu perlu ditulis ulang agar berfungsi dengan fungsi-fungsi baru, tetapi setelah membuatnya berfungsi, yang Anda tahu hanyalah bahwa tes-tes itu bekerja dengan fungsi-fungsi baru. Anda akan kehilangan tes asli dan tidak akan tahu apakah perilaku public_functelah berubah dan tes Anda pada dasarnya tidak akan berguna.

Ini adalah hal yang buruk: API yang berubah; pekerjaan dalam yang terbuka dengan erat digabungkan dengan tes; dan uji rapuh yang berubah segera setelah perubahan implementasi dilakukan.

Jadi tidak, jangan menguji fungsi pribadi. Pernah.

David Arno
sumber
Halo David, terima kasih atas jawaban Anda. Harap baca kembali pertanyaan saya, saya telah mengedit untuk mengklarifikasi.
Aviv Cohn
Ah. Tidak ada yang salah dengan menguji fungsi internal. Saya pribadi tidak menulis fungsi non-sepele apa pun kecuali saya bisa menutupinya dengan beberapa tes unit, jadi melarang pengujian fungsi internal akan cukup menghalangi saya dari menulis fungsi internal, merampok saya dari teknik yang sangat berguna. Perubahan API dapat dan lakukan; ketika mereka melakukannya, Anda harus mengubah tes. Refactoring fungsi dalam (dan pengujiannya) tidak merusak tes fungsi luar; itulah inti dari menjalani tes tersebut. Nasihat buruk secara keseluruhan.
Robert Harvey
Apa yang sebenarnya Anda perdebatkan adalah bahwa Anda seharusnya tidak memiliki fungsi pribadi.
Robert Harvey
1
@AvivCohn Mereka cukup besar untuk menjamin pengujian, dalam hal ini mereka cukup besar untuk mendapatkan file mereka sendiri; atau cukup kecil sehingga Anda tidak perlu mengujinya secara terpisah. Jadi yang mana?
Doval
3
@RobertHarvey Tidak, itu membuat argumen "memecah kelas-kelas besar menjadi komponen yang longgar jika perlu". Jika mereka benar - benar tidak umum itu mungkin kasus penggunaan yang baik untuk visibilitas paket-pribadi.
Doval