Pertanyaannya adalah apakah masuk akal untuk menulis unit test yang menyatakan bahwa utas saat ini adalah utas utama? Pro kontra?
Baru-baru ini saya telah melihat unit test yang menegaskan utas saat ini untuk panggilan balik layanan. Saya tidak yakin, itu ide yang bagus, saya percaya ini lebih seperti tes integrasi. Menurut pendapat saya, unit test harus menyatakan metode secara terpisah dan tidak boleh tahu tentang sifat konsumen layanan.
Misalnya, di iOS, pengguna layanan ini dimaksudkan untuk menjadi UI yang secara default memiliki kendala untuk menjalankan kode di utas utama.
architecture
unit-testing
object-oriented-design
multithreading
ios
Vladimir Kaltyrin
sumber
sumber
Jawaban:
Biarkan saya menunjukkan kepada Anda prinsip unit test favorit saya:
Jika pengujian unit Anda bersikeras bahwa itu adalah "utas utama" sulit untuk tidak bertabrakan dengan nomor 4.
Ini mungkin tampak keras tetapi ingat bahwa tes unit hanyalah salah satu dari berbagai jenis tes.
Anda bebas melanggar prinsip-prinsip ini. Tetapi ketika Anda melakukannya, jangan menyebut tes Anda unit test. Jangan letakkan dengan tes unit nyata dan memperlambatnya.
sumber
Haruskah Anda menulis unit test yang menguji apakah utas adalah utas utama? Tergantung, tapi mungkin bukan ide yang bagus.
Jika titik unit test dimaksudkan untuk memverifikasi berfungsinya suatu metode, maka pengujian aspek ini hampir pasti tidak menguji aspek ini. Seperti yang Anda katakan sendiri, secara konseptual lebih merupakan tes integrasi, karena berfungsinya suatu metode tidak akan berubah berdasarkan pada thread mana yang menjalankannya, dan yang bertanggung jawab untuk menentukan thread mana yang digunakan adalah pemanggil dan bukan metode itu sendiri.
Namun, inilah mengapa saya mengatakan itu tergantung, karena mungkin sangat tergantung pada metode itu sendiri, harus metode menelurkan thread baru. Meskipun dalam semua kemungkinan ini bukan kasus Anda.
Saran saya akan meninggalkan cek ini keluar dari uji unit dan port ke tes integrasi yang tepat untuk aspek ini.
sumber
Ketika seorang konsumen mengkonsumsi suatu layanan ada "kontrak" yang tersurat atau tersirat tentang fungsi apa yang disediakan dan bagaimana. Batasan pada utas apa callback dapat terjadi adalah bagian dari kontrak itu.
Saya kira kode Anda berjalan dalam konteks di mana ada semacam "mesin acara" yang berjalan di "utas utama" dan cara bagi utas lain untuk meminta agar kode jadwal mesin acara dijalankan pada utas utama.
Dalam tampilan tes unit yang paling murni, Anda akan mengejek sepenuhnya setiap ketergantungan. Di dunia nyata sangat sedikit orang melakukan itu. Kode yang sedang diuji diizinkan untuk menggunakan versi nyata dari setidaknya fitur platform dasar.
Jadi pertanyaan sebenarnya IMO adalah apakah Anda menganggap mesin acara dan dukungan threading runtime menjadi bagian dari "fitur platform dasar" yang unit tes diizinkan untuk bergantung pada atau tidak? Jika Anda melakukannya maka masuk akal untuk memeriksa apakah panggilan balik terjadi pada "utas utama". Jika Anda mengejek acara mesin dan sistem threading maka Anda akan merancang tiruan Anda sehingga mereka dapat menentukan apakah panggilan balik akan terjadi pada utas utama. Jika Anda mengejek acara mesin tetapi bukan dukungan threading runtime Anda ingin menguji apakah panggilan balik terjadi pada utas tempat mesin acara tiruan Anda berjalan.
sumber
Saya bisa melihat mengapa itu akan menjadi ujian yang menarik. Tapi apa yang sebenarnya akan diuji? katakanlah kita memiliki fungsi
Sekarang saya bisa menguji ini dan menjalankannya di utas non UI, membuktikan bahwa ada kesalahan. Tapi sungguh untuk tes yang memiliki nilai fungsi perlu memiliki beberapa perilaku spesifik yang Anda ingin lakukan ketika dijalankan di utas non-UI.
Sekarang saya memiliki sesuatu untuk diuji, tetapi tidak jelas apakah alternatif semacam ini akan berguna dalam kehidupan nyata
sumber
Jika panggilan balik ini didokumentasikan tidak aman untuk dipanggil selain dari, katakanlah, utas UI atau utas utama, maka tampaknya masuk akal dalam unit test modul kode yang menyebabkan permohonan panggilan balik ini untuk memastikan itu tidak melakukan sesuatu yang tidak aman dengan bekerja di luar utas bahwa OS atau kerangka kerja aplikasi didokumentasikan untuk menjamin aman.
sumber
Susun Penegasan
Undang-Undang
Haruskah tes unit menyatakan bahwa itu ada di utas tertentu? Tidak, karena itu tidak menguji unit dalam kasus itu, itu bahkan bukan tes integrasi, karena tidak mengatakan apa-apa tentang konfigurasi yang terjadi di unit ...
Menegaskan di mana utas pengujian aktif, akan seperti Menegaskan nama server pengujian Anda, atau lebih baik lagi nama metode pengujian.
Sekarang, mungkin masuk akal untuk mengatur agar tes dijalankan pada utas tertentu, tetapi hanya jika Anda ingin memastikan bahwa itu akan mengembalikan hasil spesifik berdasarkan utas.
sumber
Jika Anda memiliki fungsi yang didokumentasikan untuk hanya berjalan dengan benar di utas utama, dan fungsi itu disebut di utas latar belakang, itu bukan kesalahan fungsi. Kesalahannya adalah kesalahan pemanggil, sehingga pengujian unit tidak ada gunanya.
Jika kontrak diubah sehingga fungsi akan berjalan dengan benar di utas utama, dan melaporkan kegagalan saat dijalankan pada utas latar, Anda dapat menulis unit test untuk itu, di mana Anda menyebutnya sekali pada utas utama dan kemudian pada utas latar belakang dan periksa apakah berperilaku seperti yang didokumentasikan. Jadi sekarang Anda memiliki unit test, tetapi bukan unit test yang memeriksa apakah itu dijalankan pada utas utama.
Saya akan sangat menyarankan bahwa baris pertama dari fungsi Anda harus menyatakan bahwa itu berjalan di utas utama.
sumber
Pengujian unit hanya berguna jika menguji implementasi yang benar dari fungsi tersebut, bukan penggunaan yang benar, karena pengujian unit tidak dapat mengatakan bahwa suatu fungsi tidak akan dipanggil dari utas lain di seluruh basis kode Anda, seperti yang bisa dilakukan ' t mengatakan bahwa fungsi tidak akan dipanggil dengan parameter yang melanggar prasyaratnya (dan secara teknis apa yang Anda coba uji pada dasarnya merupakan pelanggaran prasyarat dalam penggunaan, yang merupakan sesuatu yang tidak dapat Anda uji secara efektif karena tes dapat ' t membatasi cara tempat lain dalam basis kode menggunakan fungsi tersebut; Anda dapat menguji apakah pelanggaran terhadap prasyarat menghasilkan kesalahan / pengecualian yang wajar, namun).
Ini adalah kasus penegasan bagi saya dalam implementasi fungsi yang relevan sendiri seperti yang beberapa orang lain tunjukkan, atau bahkan lebih canggih adalah untuk memastikan fungsi tersebut aman dari thread (meskipun ini tidak selalu praktis ketika bekerja dengan beberapa API).
Juga hanya catatan samping tapi "utas utama"! = "Utas UI" dalam semua kasus. Banyak API GUI yang tidak aman-utas (dan membuat kit GUI yang aman-utas sangat sulit), tetapi itu tidak berarti Anda harus memintanya dari utas yang sama dengan yang memiliki titik masuk untuk aplikasi Anda. Itu mungkin berguna bahkan dalam mengimplementasikan pernyataan Anda di dalam fungsi UI yang relevan untuk membedakan "utas UI" dari "utas utama", seperti menangkap ID utas saat ini ketika sebuah jendela dibuat untuk dibandingkan dengan bukan dari titik masuk utama aplikasi (yang setidaknya mengurangi jumlah asumsi / batasan penggunaan yang diterapkan hanya oleh apa yang benar-benar relevan).
Keamanan benang sebenarnya adalah titik pijakan "gotcha" di bekas tim saya, dan dalam kasus khusus kami, saya akan menyebutnya sebagai "optimasi mikro" yang paling kontra-produktif di antara mereka semua yang menghasilkan lebih banyak biaya perawatan daripada rakitan tulisan tangan. Kami memiliki cakupan kode yang agak komprehensif dalam pengujian unit kami, bersama dengan tes integrasi yang agak canggih, hanya untuk menghadapi kebuntuan dan kondisi balapan dalam perangkat lunak yang menghindari pengujian kami. Dan itu karena pengembang kode multithreaded sembarangan tanpa menyadari setiap efek samping yang mungkin terjadi dalam rantai panggilan fungsi yang akan dihasilkan dari mereka sendiri, dengan ide yang agak naif bahwa mereka dapat memperbaiki bug tersebut di belakang dengan hanya melempar mengunci di kiri dan kanan,
Bagi saya cara multithreading yang pertama dan memperbaiki bug kemudian adalah strategi yang mengerikan untuk multithreading sampai hampir membuat saya membenci multithreading pada awalnya; Anda juga memastikan desain Anda aman dari benang-solid dan implementasinya hanya menggunakan fungsi dengan jaminan yang sama (mis: fungsi murni), atau Anda menghindari multithreading. Itu mungkin sedikit dogmatis tetapi mengalahkan menemukan (atau lebih buruk, tidak menemukan) masalah yang sulit untuk direproduksi di belakang yang menghindari tes. Tidak ada gunanya mengoptimalkan mesin roket jika itu akan membuatnya rentan meledak tiba-tiba di tengah jalan sepanjang perjalanannya menuju ruang angkasa.
Jika Anda mau tidak mau harus bekerja dengan kode yang tidak aman thread, maka saya tidak melihat itu sebagai masalah untuk diselesaikan dengan pengujian unit / integrasi begitu banyak. Yang ideal adalah membatasi akses . Jika kode GUI Anda dipisahkan dari logika bisnis, maka Anda mungkin dapat menerapkan desain yang membatasi akses ke panggilan tersebut dari apa pun selain utas / objek yang membuatnya *. Mode yang jauh ideal bagi saya adalah membuatnya tidak mungkin untuk utas lain memanggil fungsi-fungsi itu daripada mencoba memastikannya tidak.
sumber