Saya telah membaca sedikit tentang Pemrograman Literate baru-baru ini, dan itu membuat saya berpikir ... Tes yang ditulis dengan baik, terutama spesifikasi gaya BDD dapat melakukan pekerjaan yang lebih baik dalam menjelaskan kode apa yang dilakukan daripada prosa, dan memiliki keuntungan besar dari memverifikasi akurasi mereka sendiri.
Saya belum pernah melihat tes yang ditulis sesuai dengan kode yang mereka uji. Apakah ini hanya karena bahasa tidak cenderung membuatnya mudah untuk memisahkan aplikasi dan kode uji ketika ditulis dalam file sumber yang sama (dan tidak ada yang membuatnya mudah), atau adakah alasan yang lebih berprinsip bahwa orang memisahkan kode uji dari kode aplikasi?
testing
unit-testing
bdd
literate-programming
Chris Devereux
sumber
sumber
Jawaban:
Satu-satunya keuntungan yang dapat saya pikirkan untuk tes inline adalah mengurangi jumlah file yang akan ditulis. Dengan IDE modern ini sebenarnya bukan masalah besar.
Namun, ada beberapa kelemahan yang jelas untuk pengujian inline:
sumber
Saya dapat memikirkan beberapa hal:
Keterbacaan. Menyelingi kode "nyata" dan tes akan membuatnya lebih sulit untuk membaca kode nyata.
Kode mengasapi. Mencampur kode "nyata" dan kode uji ke dalam file / kelas yang sama / apa pun yang mungkin menghasilkan file yang dikompilasi lebih besar, dll. Ini sangat penting untuk bahasa dengan ikatan yang terlambat.
Anda mungkin tidak ingin pelanggan / klien Anda melihat kode pengujian Anda. (Saya tidak suka alasan ini ... tetapi jika Anda bekerja pada proyek sumber tertutup, kode uji kemungkinan tidak akan membantu pelanggan.)
Sekarang ada solusi yang mungkin untuk masing-masing masalah ini. Tapi IMO, lebih mudah untuk tidak pergi ke sana sejak awal.
Patut diperhatikan bahwa pada masa-masa awal, programmer Java biasa melakukan hal semacam ini; misalnya memasukkan
main(...)
metode dalam kelas untuk memfasilitasi pengujian. Gagasan ini hampir sepenuhnya menghilang. Ini adalah praktik industri untuk mengimplementasikan tes secara terpisah menggunakan semacam kerangka uji.Perlu juga diperhatikan bahwa Pemrograman Literate (seperti yang dikandung oleh Knuth) tidak pernah tertangkap dalam industri rekayasa perangkat lunak.
sumber
Sebenarnya, Anda dapat menganggap Desain Dengan Kontrak sebagai melakukan ini. Masalahnya adalah sebagian besar bahasa pemrograman tidak membiarkan Anda menulis kode seperti ini :( Sangat mudah untuk menguji prasyarat dengan tangan, tetapi kondisi posting adalah tantangan nyata tanpa mengubah cara Anda menulis kode (IMO negatif besar).
Michael Feathers memiliki presentasi tentang ini dan ini adalah salah satu dari banyak cara dia menyebutkan Anda dapat meningkatkan kualitas kode.
sumber
Untuk banyak alasan yang sama ketika Anda mencoba untuk menghindari kopling ketat antara kelas-kelas dalam kode Anda, juga merupakan ide yang baik untuk menghindari kopling yang tidak perlu antara tes dan kode.
Penciptaan: Tes dan kode dapat ditulis pada waktu yang berbeda, oleh orang yang berbeda.
Kontrol: Jika tes digunakan untuk menentukan persyaratan, Anda tentu menginginkannya tunduk pada aturan yang berbeda tentang siapa yang dapat mengubahnya dan kapan dibandingkan dengan kode yang sebenarnya.
Dapat digunakan kembali: Jika Anda menempatkan tes sebaris, Anda tidak dapat menggunakannya dengan potongan kode lain.
Bayangkan Anda memiliki banyak kode yang melakukan pekerjaan dengan benar, tetapi masih banyak yang diinginkan dalam hal kinerja, pemeliharaan, apa pun. Anda memutuskan untuk mengganti kode itu dengan kode baru dan lebih baik. Menggunakan serangkaian tes yang sama dapat membantu Anda memverifikasi bahwa kode baru menghasilkan hasil yang sama dengan kode lama.
Kemampuan pilih : Menjaga tes terpisah dari kode membuatnya lebih mudah untuk memilih tes mana yang ingin Anda jalankan.
Misalnya, Anda mungkin memiliki serangkaian kecil tes yang hanya terkait dengan kode yang sedang Anda kerjakan, dan suite yang lebih besar yang menguji seluruh proyek.
sumber
Berikut adalah beberapa alasan tambahan yang dapat saya pikirkan:
memiliki tes di perpustakaan terpisah membuatnya lebih mudah untuk menautkan hanya perpustakaan itu terhadap kerangka pengujian Anda, dan bukan kode produksi Anda (ini bisa dihindari oleh beberapa preprocessor, tetapi mengapa membangun hal seperti itu ketika solusi yang lebih mudah adalah dengan menulis tes di tempat terpisah)
tes fungsi, kelas, pustaka biasanya ditulis dari sudut pandang "pengguna" (pengguna fungsi / kelas / pustaka). "Menggunakan kode" seperti itu biasanya ditulis dalam file atau pustaka yang terpisah, dan tes mungkin lebih jelas atau "lebih realistis" jika meniru situasi itu.
sumber
Jika pengujian sesuai, akan diperlukan untuk menghapus kode yang Anda butuhkan untuk pengujian ketika Anda mengirimkan produk ke pelanggan Anda. Jadi tempat tambahan di mana Anda menyimpan tes Anda hanya memisahkan antara kode yang Anda butuhkan dan kode yang dibutuhkan pelanggan Anda .
sumber
Ide ini sama dengan metode "Self_Test" dalam konteks desain berbasis objek atau berorientasi objek. Jika menggunakan bahasa berbasis objek yang dikompilasi seperti Ada, semua kode swa-uji akan ditandai oleh kompiler sebagai tidak digunakan (tidak pernah dipanggil) selama kompilasi produksi, dan oleh karena itu semua akan dioptimalkan jauh - tidak ada yang akan muncul di dapat dieksekusi.
Menggunakan metode "Self_Test" adalah ide yang sangat bagus, dan jika programmer benar-benar peduli dengan kualitas, mereka semua akan melakukannya. Namun, satu masalah penting adalah bahwa metode "Self_Test" perlu memiliki disiplin yang kuat, karena metode ini tidak dapat mengakses detail implementasi apa pun dan harus mengandalkan hanya pada semua metode lain yang dipublikasikan dalam spesifikasi objek. Jelas, jika tes mandiri gagal, implementasinya perlu diubah. Tes mandiri harus secara ketat menguji semua properti yang dipublikasikan dari metode objek, tetapi tidak pernah mengandalkan cara apa pun pada detail implementasi apa pun yang spesifik.
Bahasa berbasis objek dan berorientasi objek sering menyediakan jenis disiplin yang tepat sehubungan dengan metode eksternal untuk objek yang diuji (mereka menegakkan spesifikasi objek, mencegah akses ke detail implementasi dan meningkatkan kesalahan kompilasi jika upaya tersebut terdeteksi ). Tetapi metode internal objek itu sendiri semua diberikan akses lengkap ke setiap detail implementasi. Jadi metode uji diri berada dalam situasi yang unik: perlu metode internal karena sifatnya (uji diri jelas merupakan metode objek yang diuji), namun perlu menerima semua disiplin kompilator dari metode eksternal ( itu harus independen dari rincian implementasi objek). Sedikit jika ada bahasa pemrograman memberikan kemampuan untuk mendisiplinkan objek ' Metode internal seolah-olah itu adalah metode eksternal. Jadi ini adalah masalah desain bahasa pemrograman yang penting.
Dengan tidak adanya dukungan bahasa pemrograman yang tepat, cara terbaik untuk melakukannya adalah dengan membuat objek pendamping. Dengan kata lain, untuk setiap objek yang Anda kode (sebut saja "Big_Object"), Anda juga membuat objek pendamping kedua yang namanya terdiri dari akhiran standar yang disatukan dengan nama objek "nyata" (dalam hal ini, "Big_Object_Self_Test" "), dan yang spesifikasinya terdiri dari metode tunggal (" Big_Object_Self_Test.Self_Test (This_Big_Object: Big_Object) mengembalikan Boolean; "). Objek pendamping kemudian akan tergantung pada spesifikasi objek utama, dan kompiler akan sepenuhnya menegakkan semua disiplin spesifikasi itu terhadap implementasi objek pendamping.
sumber
Ini sebagai tanggapan terhadap sejumlah besar komentar yang menunjukkan bahwa tes inline tidak dilakukan karena sulit untuk tidak mungkin menghapus kode tes dari rilis rilis. Ini tidak benar. Hampir semua kompiler dan assembler sudah mendukung ini, dengan bahasa yang dikompilasi, seperti C, C ++, C #, ini dilakukan dengan apa yang disebut arahan kompiler.
Dalam kasus c # (saya percaya c ++ juga, sintaks mungkin sedikit berbeda tergantung pada apa yang Anda gunakan kompiler) ini adalah bagaimana Anda dapat melakukannya.
Karena ini menggunakan arahan kompiler, kode tidak akan ada dalam file yang dapat dieksekusi yang dibangun jika flag tidak disetel. Ini juga cara Anda membuat program "tulis sekali, kompilasi dua kali" untuk beberapa platform / perangkat keras.
sumber
Kami menggunakan tes sebaris dengan kode Perl kami. Ada modul, Test :: Inline , yang menghasilkan file uji dari kode inline.
Saya tidak terlalu pandai mengatur tes, dan merasa lebih mudah dan lebih mungkin dipertahankan saat digarisbawahi.
Menanggapi beberapa masalah yang dikemukakan:
+-- 33 lines: #test----
. Saat Anda ingin mengerjakan tes, Anda cukup mengembangkannya.Sebagai referensi:
sumber
Erlang 2 sebenarnya mendukung tes inline. Setiap ekspresi boolean dalam kode yang tidak digunakan (misalnya ditugaskan ke variabel atau lulus) secara otomatis diperlakukan sebagai tes dan dievaluasi oleh kompiler; jika ekspresi salah, kode tidak dikompilasi.
sumber
Alasan lain untuk memisahkan tes adalah bahwa Anda sering menggunakan pustaka tambahan atau bahkan berbeda untuk pengujian daripada untuk implementasi yang sebenarnya. Jika Anda mencampur tes dan implementasi, penggunaan pustaka uji secara tidak sengaja dalam implementasi tidak dapat ditangkap oleh kompiler.
Selain itu, tes cenderung memiliki lebih banyak baris kode daripada bagian implementasi yang diuji, sehingga Anda akan kesulitan menemukan implementasi di antara semua tes. :-)
sumber
Ini tidak benar. Jauh lebih baik untuk menempatkan unit-tes Anda di samping kode produksi ketika kode produksi terutama ketika rutinitas produksi murni.
Misalnya, jika Anda mengembangkan di bawah .NET, Anda dapat memasukkan kode pengujian dalam unit produksi, dan kemudian menggunakan Scalpel untuk menghapusnya sebelum mengirim.
sumber