Apa kerugian dari pemrograman test-first?

47

Ini semua kemarahan saat ini. "Semua orang" merekomendasikannya. Itu dengan sendirinya membuatku curiga.

Apa saja kerugian yang Anda temukan ketika melakukan pengembangan test-first (test-driven)? Saya mencari pengalaman pribadi dari para praktisi yang berpengetahuan luas - saya dapat membaca renungan hipotetis dari seratus wannabes di tempat lain di internet.

Saya bertanya bukan karena saya ingin membenci TDD, tetapi karena itu adalah tugas saya untuk meningkatkan proses pengembangan perangkat lunak, dan semakin banyak yang dapat kita pelajari tentang masalah yang dihadapi orang, semakin besar peluang yang kita miliki untuk memperbaiki proses.

Alex Feinman
sumber

Jawaban:

41

Ada beberapa, tetapi keuntungannya jauh melebihi kerugiannya.

Ada kurva belajar yang curam.

Banyak pengembang tampaknya berharap bahwa mereka dapat efisien dengan pemrograman uji pertama sejak hari pertama. Sayangnya butuh banyak waktu untuk mendapatkan pengalaman dan program dengan kecepatan yang sama seperti sebelumnya. Anda tidak bisa menyiasatinya.

Untuk lebih spesifik, sangat mudah salah. Anda dapat dengan mudah (dengan niat yang sangat baik) akhirnya menulis sejumlah tes yang sulit untuk mempertahankan atau menguji hal-hal yang salah. Sulit untuk memberikan contoh di sini - masalah seperti ini hanya membutuhkan pengalaman untuk dipecahkan. Anda harus memiliki perasaan yang baik untuk memisahkan masalah dan merancang untuk diuji. Saran terbaik saya di sini adalah melakukan pemrograman pasangan dengan seseorang yang mengenal TDD dengan sangat baik.

Anda melakukan lebih banyak coding di muka.

Tes-pertama berarti Anda tidak dapat melewati tes (yang bagus) dan berarti Anda akan berakhir dengan menulis lebih banyak kode di muka. Ini berarti lebih banyak waktu. Sekali lagi, Anda tidak bisa menyiasatinya. Anda mendapatkan imbalan dengan kode yang lebih mudah dipelihara, diperluas, dan umumnya lebih sedikit bug, tetapi butuh waktu.

Bisa menjadi penjualan yang sulit bagi para manajer.

Manajer perangkat lunak pada umumnya hanya mementingkan waktu. Jika Anda beralih ke pemrograman pengujian pertama dan Anda tiba-tiba membutuhkan waktu 2 minggu untuk menyelesaikan fitur, bukan satu, mereka tidak akan menyukainya. Ini jelas merupakan pertempuran yang layak diperjuangkan dan banyak manajer yang cukup tercerahkan untuk mendapatkannya, tetapi ini bisa menjadi penjualan yang sulit.

Bisa menjadi penjualan yang sulit untuk sesama pengembang.

Karena ada kurva belajar yang curam, tidak semua pengembang menyukai pemrograman uji-pertama. Bahkan, saya kira sebagian besar pengembang tidak menyukainya pada awalnya. Anda dapat melakukan hal-hal seperti pemrograman pasangan untuk membantu mereka mencapai kecepatan, tetapi itu bisa menjadi penjualan yang sulit.

Pada akhirnya, keuntungannya melebihi kerugiannya, tetapi itu tidak membantu jika Anda mengabaikan kerugiannya. Mengetahui apa yang Anda hadapi sejak awal membantu Anda menegosiasikan beberapa kelemahan, jika tidak semua.

Jaco Pretorius
sumber
Ini adalah jawaban yang baik, tetapi bisa lebih spesifik tentang # 1? Saya terutama tertarik mendengar bagaimana / apakah Anda dapat memulihkan kecepatan pemrograman Anda - apa yang Anda pelajari yang tidak Anda ketahui ketika Anda mulai melakukan TDD?
Alex Feinman
Diperbarui untuk memberikan klarifikasi
Jaco Pretorius
7
Jika Anda melakukan pengujian sekarang, total waktu yang dihabiskan dalam pengembangan seharusnya tidak berubah secara signifikan. Ini hanya terlihat seperti hal-hal yang memakan waktu lebih lama karena Anda mengekspos waktu yang diperlukan untuk menulis dan mempertahankan tes unit.
ChrisF
1
@ Jeff, apakah Anda terbiasa dengan "Saya akan menulis sendiri minivan!" sekolah coding?
Alex Feinman
1
@vanfosson - karena mereka mencoba mengubah dua hal sekaligus - baik memulai pengujian dan juga TDD - yang bisa menjadi masalah. Ini juga menambah perkiraan waktu - cukup benar - sehingga manajer dan pelanggan hanya melihat peningkatan di muka, bukan karena keseluruhan waktu sebenarnya diketahui (untuk sekali) dan bahkan mungkin kurang. Jika mereka melakukan pengujian maka peningkatan ini akan lebih sedikit.
ChrisF
35

Tes-pertama mengasumsikan Anda menulis kode yaitu:

  • diuji dalam cara unit-test
  • bahwa apa yang Anda kembangkan memiliki pendekatan yang jelas dan tidak akan memerlukan prototipe atau eksperimen yang luas
  • Anda tidak perlu melakukan refactor terlalu banyak atau Anda punya waktu untuk menulis ulang ratusan atau ribuan kasus uji berulang kali
  • tidak ada yang disegel
  • semuanya modular
  • semuanya bisa disuntikkan atau bisa dipermainkan
  • bahwa organisasi Anda menempatkan nilai yang cukup tinggi pada cacat rendah untuk membenarkan sumber daya yang tenggelam
  • bahwa ada sesuatu yang bermanfaat untuk diuji pada tingkat unit test

Jika proyek Anda tidak memenuhi persyaratan tersebut, Anda akan mengalami kesulitan. Promotor TDD tidak memiliki jawaban yang baik untuk yang lain ini untuk menyarankan agar Anda mendesain ulang produk Anda agar lebih sesuai dengan garis-garis itu. Ada situasi di mana itu tidak mungkin atau tidak diinginkan.

Dalam praktiknya, saya juga bisa mengalami masalah besar dengan orang-orang yang berpikir tes pertama benar-benar membuktikan apa pun tentang fungsi program yang benar. Dalam banyak kasus ini tidak benar, tetapi bahkan dalam kasus di mana itu benar itu jauh dari gambaran lengkap tentang kebenaran. Orang melihat ratusan tes yang lulus dan menganggap aman untuk menguji kurang karena sebelum TDD mereka hanya melakukan beberapa ratus kasus uji. Dalam pengalaman saya, TDD berarti bahwa Anda perlu memiliki lebih banyak tes integrasi karena pengembang juga akan memiliki keamanan palsu dan rasa sakit karena mengubah semua tes untuk melakukan redactor besar dapat membuat pengembang membuat pekerjaan yang menarik di sekitarnya.

Contoh:

Contoh terbaik pribadi saya adalah ketika menulis kode keamanan untuk asp.net. Jika mereka dimaksudkan untuk berjalan dalam lingkungan yang tidak bersahabat dari konfigurasi mesin, mereka dihadang, ditandatangani dan disegel, dan karena mereka berlari melawan benda-benda dewa IIS, mereka sangat sulit untuk diejek dengan sangat benar. Tambahkan beberapa kendala untuk kinerja dan penggunaan memori dan Anda dengan cepat kehilangan fleksibilitas untuk menggunakan objek placeholder di area yang tersisa.

Segala jenis pengontrol mikro atau kode lingkungan sumber daya rendah lainnya tidak mungkin dilakukan untuk benar-benar mendesain gaya OO karena abstraksi tidak dioptimalkan dan Anda memiliki batas sumber daya yang rendah. Hal yang sama dapat dikatakan untuk rutinitas kinerja tinggi dalam banyak kasus juga.

Tagihan
sumber
Bisakah Anda memberikan beberapa contoh tandingan? Kapan saya tidak akan menulis sesuatu yang dapat diuji dengan cara unit-test? Mengapa saya tidak menulis kode yang bisa dipermainkan atau disuntikkan (selain kode warisan, yang merupakan topik tersendiri)?
Alex Feinman
diedit untuk menambahkan bagian contoh
Bill
4
Sepakat. Bekerja TDD tampaknya bergantung pada serangkaian asumsi tentang mesin yang Anda kerjakan; tampaknya tidak berlaku untuk sekitar 50% dari proyek saya.
Paul Nathan
Saya sangat setuju ... Jawaban bagus
Khelben
2
Ini seperti apa pun dalam game ini - sesuai untuk banyak situasi, tidak sesuai untuk orang lain. Waspadalah terhadap siapa pun yang menganjurkan One True Path di bidang pengembangan perangkat lunak apa pun .
Alan B
25

Kelemahan terbesar yang saya lihat bukan dengan TDD itu sendiri tetapi dengan praktisi. Mereka mengambil pendekatan dogmatis dan fanatik di mana semuanya harus diuji . Terkadang (berkali-kali itu), itu tidak perlu. Juga, ini mungkin tidak praktis (mis. Memperkenalkan organisasi ke TDD.)

Seorang insinyur yang baik menemukan pengorbanan dan menerapkan keseimbangan yang tepat kapan / di mana / bagaimana menerapkan tes-pertama. Juga, jika Anda mendapati diri Anda terus-menerus menghabiskan lebih banyak waktu mengembangkan tes daripada kode yang sebenarnya (dengan faktor 2-3 atau lebih), Anda dalam masalah.

Dengan kata lain, bersikap pragmatis dan masuk akal dengan TDD (atau apa pun dalam pengembangan perangkat lunak dalam hal ini.)

luis.espinal
sumber
Apakah itu, mungkin, dari mana definisi "baru" Michael Feathers tentang Kode Warisan (yaitu "Kode tanpa Tes") berasal?
Phill W.
Definisi itu tidak akan bekerja untuk saya :) Bagi saya, kode apa pun yang berjalan dalam produksi dan yang dapat berubah adalah kode lama, terlepas dari kode atau kualitas pengujian. Kami biasanya mengaitkan "kode lama" dengan "kode buruk" atau "kode usang" ketika dalam kenyataannya kode buruk dan kode usang sudah ada dalam kode dalam pengembangan yang belum melihat penggunaan produksi. Tujuan kita seharusnya adalah agar kode kita menjadi warisan sejak awal, dan menjadi kualitas dan kegunaan sedemikian rupa sehingga tetap digunakan selama bertahun-tahun, dekade mendatang.
luis.espinal
6

Saya mulai melakukan TDD pada awal Agustus 2009 dan meyakinkan seluruh perusahaan saya untuk beralih ke hal itu pada bulan September / Oktober 2009. Saat ini, seluruh tim dev sepenuhnya dikonversi, dan melakukan kode yang belum diuji ke repo dianggap sebagai Hal Buruk dan dibuang. Ini telah bekerja dengan baik bagi kami, dan saya tidak bisa membayangkan beralih kembali ke pengkodean koboi.

Namun, ada dua masalah yang cukup mencolok.

Test suite harus dipertahankan

Ketika Anda serius tentang TDD, Anda akan berakhir dengan menulis banyak tes. Selain itu, perlu beberapa waktu dan pengalaman untuk menyadari apa granularity yang tepat dari tes (overdoing hampir sama buruknya dengan underdoing). Tes-tes ini juga merupakan kode , dan mereka rentan terhadap bitrot. Ini berarti Anda harus mempertahankannya seperti yang lainnya: perbarui ketika Anda memutakhirkan pustaka yang menjadi andalannya, refactor dari waktu ke waktu ... Saat Anda membuat perubahan besar pada kode Anda, banyak tes tiba-tiba akan menjadi ketinggalan zaman atau bahkan jelas salah. Jika Anda beruntung, Anda bisa menghapusnya, tetapi sering kali Anda akan mengekstraksi bit yang berguna dan mengadaptasinya ke arsitektur baru.

Abstraksi pengujian bocor dari waktu ke waktu

Kami menggunakan Django, yang memiliki kerangka pengujian yang cukup bagus. Namun, terkadang ia membuat asumsi yang sedikit bertentangan dengan kenyataan. Misalnya, beberapa middleware dapat merusak tes. Atau, beberapa tes membuat asumsi tentang backend caching. Juga, jika Anda menggunakan "nyata" db (bukan SQLite3), maka mempersiapkan db untuk tes akan memakan banyak waktu. Tentu, Anda dapat (dan harus) menggunakan SQLite3 dan db dalam memori untuk tes yang Anda lakukan secara lokal, tetapi beberapa kode hanya akan berperilaku berbeda tergantung pada database yang Anda gunakan. Menyiapkan server integrasi berkelanjutan yang berjalan dalam pengaturan realistis adalah suatu keharusan.

(Beberapa orang akan memberi tahu Anda bahwa Anda harus mengejek semua hal seperti database, atau tes Anda tidak "murni," tetapi itu hanya berbicara tentang ideologi. Jika Anda membuat kesalahan dalam kode ejekan Anda (dan percayalah, Anda akan), testuite Anda akan menjadi tidak berharga.)

Ini semua mengatakan, masalah yang saya jelaskan mulai terlihat hanya ketika Anda cukup maju dengan TDD ... Ketika Anda baru mulai dengan TDD (atau mengerjakan proyek yang lebih kecil) tes refactoring tidak akan menjadi masalah.

Ryszard Szopa
sumber
3
+1. "harus dipertahankan": ini jauh dari masalah ketika menguji kode yang dapat digunakan kembali, karena antarmuka dan perilakunya biasanya perlu stabil. Untuk alasan ini, saya biasanya hanya melakukan TDD untuk pustaka yang dapat digunakan kembali.
Dimitri C.
4

Bagi saya ada beberapa masalah psikologis yang mendalam dengan tes setiap kali saya mencoba menerapkannya secara luas, seperti pada TDD: jika ada, saya kode sembarangan karena saya percaya bahwa tes akan menangkap masalah. Tetapi jika tidak ada tes untuk menyediakan jaring pengaman, saya kode dengan hati-hati, dan hasilnya selalu lebih baik daripada dengan tes.

Mungkin hanya aku. Tetapi saya juga pernah membaca di suatu tempat bahwa mobil dengan semua jenis lonceng & peluit cenderung lebih sering menabrak (karena pengemudi tahu bahwa fitur keselamatan ada di sana), jadi mungkin ini sesuatu yang perlu diketahui; TDD dapat tidak kompatibel dengan beberapa individu.

Joonas Pulakka
sumber
Tampaknya aneh bagi saya, karena menulis kode yang dapat diuji biasanya menyebabkan saya melambat dan berpikir lebih banyak tentang apa yang saya koding. Saya sebenarnya mendapatkan sedikit gugup tanpa tes hari ini.
Matt H
1
Itu hanya menunjukkan bahwa orang yang berbeda memang bereaksi berbeda. Saya tidak memukul TDD - jelas beberapa orang menganggapnya berguna - tetapi faktanya ini bukan untuk semua orang.
Joonas Pulakka
2
Saya setuju 100%. Saya menulis kode lebih baik dan lebih cepat tanpa tes otomatis. Tentu saja tidak masuk akal untuk tidak menguji, saya hanya berpikir otomatisasi adalah pilihan yang buruk (setidaknya untuk saya). Saya menemukan pengujian manual lebih cepat daripada mempertahankan suite pengujian dan lebih aman - tapi saya juga seorang pengembang yang berpengalaman jadi saya sangat pandai mengetahui apa yang harus diuji dan di mana dan mengapa, jadi penambahan kode dan faktor-ulang saya adalah bebas regresi.
Ben Lee
1
Walaupun saya harus menunjukkan bahwa tim tempat saya bekerja dan proyek-proyeknya cukup kecil sehingga saya memahami seluruh arsitektur - pada tim besar atau proyek yang sangat besar, saya dapat melihat tes otomatis lebih berguna, karena maka tidak ada pengembang tunggal yang dapat mencium di mana mereka harus menguji untuk menghindari regresi.
Ben Lee
Apakah Anda meninggalkan langkah refactoring?
rjnilsson
2

Satu situasi di mana tes pertama benar-benar menghalangi saya adalah ketika saya ingin dengan cepat mencoba beberapa ide dan melihat apakah itu bisa bekerja sebelum saya menulis implementasi yang tepat.

Pendekatan saya biasanya:

  1. Menerapkan sesuatu yang berjalan (proof of concept).
  2. Jika berhasil, berkonsolidasi dengan menambahkan tes, meningkatkan desain, refactoring.

Terkadang saya tidak sampai ke langkah 2.

Dalam hal ini, menggunakan TDD ternyata memiliki lebih banyak kerugian daripada keuntungan bagi saya:

  • Menulis tes selama implementasi pembuktian konsep hanya memperlambat saya dan menyela aliran pemikiran saya: Saya ingin memahami sebuah ide dan saya tidak ingin membuang waktu untuk menguji rincian implementasi kasar pertama saya.
  • Mungkin butuh waktu lebih lama untuk mengetahui apakah ide saya berharga atau tidak.
  • Jika ternyata ide itu tidak berguna, saya harus membuang kedua kode saya dan tes unit yang ditulis dengan baik.

Jadi, ketika saya harus menjelajahi beberapa ide baru, saya tidak menggunakan TDD dan hanya memperkenalkan unit test ketika saya merasa kode baru tersebut mencapai suatu tempat.

Giorgio
sumber
1
Sepertinya Anda membingungkan kode prototipe dengan kode yang dapat digunakan. Kode prototipe adalah kode uji . Itu tidak perlu diuji dan Anda tidak harus membuat tes yang berjalan melawannya. Langkah yang Anda lewatkan adalah antara 1. dan 2 .: Anda mengatakan "konsolidasi dengan menulis tes". Masalahnya adalah Anda tidak memiliki sesuatu untuk dikonsolidasikan, tetapi sesuatu untuk ditulis. Rencanakan untuk menulis ulang kode prototipe, jangan berencana untuk menggunakannya kembali. Menggunakan kembali itu menyisakan banyak tempat untuk kompromi. Penulisan ulang memformalkan pemisahan antara fase eksplorasi Anda dan fase "kode kualitas" Anda.
utnapistim
3
@utnapistim: Saya tidak mengacaukan kode prototipe dengan kode yang dapat digunakan, sebaliknya, fanatik TDD membingungkan mereka dan menyarankan Anda harus menggunakan TDD untuk kode prototipe juga. Atau lebih tepatnya, mereka menganggap bahwa tidak ada kode prototipe sama sekali. Juga, saya setuju dengan Anda bahwa sering kali Anda harus menulis ulang ketika Anda beralih dari prototipe ke implementasi nyata. Kadang-kadang Anda dapat menggunakan kembali bagian dari kode prototipe, tetapi Anda harus siap untuk menulis ulang. Anda benar-benar harus memutuskan dari kasus ke kasus.
Giorgio
3
@utnapistim: Lihat juga jawaban luis.espinal: "Kelemahan terbesar yang saya lihat bukan dengan TDD itu sendiri tetapi dengan praktisi. Mereka mengambil pendekatan dogmatis dan fanatik di mana semuanya harus diuji.".
Giorgio
1

Kekurangan atau Biaya TDD

Catatan: Ada berbagai jenis TDD. Terlepas dari unit, BDD, ATDD, atau varian lain banyak dari kesulitan tetap ada

Efek samping

Baik itu mengejek, perlengkapan, atau tes fungsional, ketergantungan pada kondisi eksternal atau sistem sering kali menjadi sumber paling rumit dalam pengujian, kebingungan dalam cara menguji, dan risiko terbesar jika salah melakukannya. Beberapa masalah yang saya lihat:

  • Mengejek: lupa menegaskan urutan panggilan
  • Mengolok-olok: mengolok-olok tidak cocok dengan panggilan atau respons nyata
  • Fixture: tes bergantung pada data yang tidak realistis, menutupi masalah lain
  • Fixture: menguji keadaan yang mustahil dalam produksi
  • Fungsional: false build break karena sistem dependen tidak tersedia untuk sementara
  • Fungsional: kecepatan tes sangat lambat

Anda harus mengubah pendekatan Anda terhadap pengkodean, untuk beberapa hal itu akan menjadi perubahan drastis.

Kode orang berbeda dengan cara yang sangat berbeda. Di TDD Anda harus mulai dengan tes yang menegaskan perilaku tertentu, dan kemudian menerapkannya sehingga tes berlalu. Saya telah melihat dan merupakan seorang programmer yang pemrogramannya tidak kondusif untuk TDD. Butuh waktu sekitar 2 bulan ketika saya awalnya mulai terbiasa mengubah pendekatan pengembangan saya.

Butuh waktu untuk memahami apa yang Anda pedulikan tentang pengujian dan apa yang tidak Anda pedulikan tentang pengujian.

Setiap tim harus membuat keputusan eksplisit tentang di mana mereka ingin menarik garis dalam pengujian. Hal-hal apa yang mereka nilai yang ingin mereka uji, dan apa yang tidak. Seringkali proses yang menyakitkan mempelajari bagaimana menulis tes yang baik, dan apa yang sebenarnya Anda pedulikan tentang pengujian. Sementara itu kode akan terus dalam keadaan fluks sampai ada konsistensi dalam gaya dan pendekatan.

Unit Test spesifik: refaktor besar

Refactor besar atau mendasar dari basis kode yang signifikan dengan puluhan ribu unit test akan menghasilkan biaya yang sangat besar untuk memperbarui semua tes. Ini akan sering terwujud dalam pushback terhadap melakukan refactor bahkan jika itu adalah hal yang benar untuk dilakukan hanya karena biaya yang terkait dengan melakukannya.

dietbuddha
sumber
0

Analogi saya adalah penghalang di jalur Scalextric. Jika Anda memakainya, Anda menjadi kurang berhati-hati.

Orang-orang juga mendapatkan sedikit ruang cadetish tentang tes mereka - karena mereka berjalan dengan baik, mereka percaya kode tersebut sepenuhnya diuji sedangkan itu hanya awal dari proses pengujian.

Dalam pikiran saya TDD adalah batu loncatan ke BDD. Rakit tes yang berjalan tidak benar-benar membantu mendukung pengembang tanpa mengetahui apa yang dilakukan tes. Dengan BDD, output tes dalam bahasa Inggris yang mendokumentasikan pengujian dan dengan demikian membangun pemahaman sistem.

Robbie Dee
sumber
-1

Manfaat TDD adalah memaksa Anda untuk menjaga kode Anda terhadap orang-orang yang tidak memahaminya. Ya, ini sering termasuk diri Anda sendiri. Tapi, apa yang terjadi ketika kode tidak layak dijaga? Ada banyak kode yang seharusnya tidak ada di sana! Jadi masalah dengan TDD adalah ketika datang ke pengembang yang menulis kode buruk. TDD mungkin tidak akan membantu mereka menulis kode yang baik, kemungkinan besar mereka akan menulis tes yang mengerikan juga. Jadi dalam kasus mereka TDD hanya akan menambah kekacauan; tes yang ditulis dengan buruk dan / atau redundan tidak lebih menyenangkan daripada bentuk kode buruk lainnya.

Johan
sumber
1
Jika Anda sendiri tidak memahami kode Anda sendiri, bagaimana segelintir di antara miliaran kemungkinan testis dapat melindungi terhadap kode yang salah?
Michael Shaw
2
Karena Anda memahaminya ketika Anda menulisnya tetapi lupa tentang itu di sepanjang jalan?
Johan
+1 TDD tidak melindungi terhadap pengembang yang salah paham tentang persyaratan bisnis. Di sinilah BDD masuk ...
Robbie Dee