Cara melakukan Pengembangan Berbasis Tes

15

Saya hanya memiliki 2+ tahun pengalaman dalam pengembangan aplikasi. Dalam dua tahun itu pendekatan saya terhadap pembangunan adalah sebagai berikut

  1. Menganalisis persyaratan
  2. Komponen / Objek Inti Identitas, Fungsi yang diperlukan, Perilaku, Proses dan batasannya
  3. Buat kelas, hubungan di antara mereka, batasan pada objek perilaku & negara
  4. Buat fungsi, proses dengan batasan perilaku sesuai persyaratan
  5. Aplikasi uji secara manual
  6. Jika perubahan persyaratan memodifikasi komponen / fungsi, maka uji aplikasi secara manual

Baru-baru ini saya diperkenalkan ke TDD dan merasa bahwa ini adalah cara yang sangat baik untuk melakukan pengembangan karena kode yang dikembangkan memiliki alasan kuat untuk ada dan banyak masalah pasca penempatan dikurangi.

Tetapi masalah saya adalah saya tidak dapat membuat tes terlebih dahulu, Alih-alih saya mengidentifikasi komponen dan hanya menulis tes untuk mereka sebelum saya benar-benar menulis komponen. pertanyaanku adalah

  1. Apakah saya melakukan itu benar? Kalau bukan apa tepatnya aku harus berubah
  2. Apakah ada cara Anda dapat mengidentifikasi apakah tes yang Anda tulis sudah cukup?
  3. Apakah praktik yang baik untuk menulis tes untuk fungsionalitas yang sangat sederhana yang mungkin setara dengan 1 + 1 = 2 atau hanya overplay?
  4. Apakah baik untuk mengubah fungsionalitas dan menguji apakah persyaratan berubah?
Yogesh
sumber
2
"Saya mengidentifikasi komponen dan hanya menulis tes untuk mereka sebelum saya benar-benar menulis komponen.": Saya menemukan ini benar: Anda pertama-tama mengidentifikasi arsitektur kasar sistem Anda dan kemudian mulai pengkodean. Selama pengkodean (TDD) Anda mengerjakan rincian komponen individual dan mungkin menemukan masalah dengan arsitektur Anda yang dapat Anda perbaiki sepanjang jalan. Tetapi saya merasa OK bahwa Anda tidak memulai pengkodean tanpa analisis sebelumnya.
Giorgio
Anda juga dapat melakukan pengujian unit / integrasi otomatis tanpa melakukan TDD. Keduanya sering bingung, tetapi mereka tidak sama.
Andres F.

Jawaban:

19

Apakah saya melakukan itu benar? Kalau bukan apa tepatnya aku harus berubah

Sulit dikatakan hanya dari uraian singkat itu, tetapi saya curiga, tidak, Anda tidak melakukannya dengan benar. Catatan: Saya tidak mengatakan bahwa apa yang Anda lakukan tidak berfungsi atau dalam beberapa hal buruk, tetapi Anda tidak melakukan TDD. Bagian tengah "D" berarti "Didorong", tes menggerakkan segalanya, proses pengembangan, kode, desain, arsitektur, semuanya .

Tes memberi tahu Anda apa yang harus ditulis, kapan harus menulis, apa yang harus ditulis selanjutnya, kapan harus berhenti menulis. Mereka memberi tahu Anda desain dan arsitekturnya. (Desain dan arsitektur muncul dari kode melalui refactoring.) TDD bukan tentang pengujian. Ini bahkan bukan tentang menulis tes pertama: TDD adalah tentang membiarkan tes membuat Anda, menulis mereka terlebih dahulu hanyalah prasyarat yang diperlukan untuk itu.

Tidak masalah apakah Anda benar-benar menulis kode, atau membuatnya sepenuhnya berubah: Anda menulis kode (kerangka) di kepala Anda, kemudian menulis tes untuk kode itu. Itu bukan TDD.

Melepaskan kebiasaan itu sulit . Sangat, sangat sulit. Tampaknya sangat sulit bagi programmer berpengalaman.

Keith Braithwaite telah menciptakan latihan yang dia sebut TDD As If You Meant It . Ini terdiri dari seperangkat aturan (berdasarkan Tiga Aturan TDD Paman Bob Martin , tetapi jauh lebih ketat) yang harus Anda ikuti dengan ketat dan yang dirancang untuk mengarahkan Anda untuk menerapkan TDD dengan lebih ketat. Ini bekerja paling baik dengan pemrograman pasangan (sehingga pasangan Anda dapat memastikan Anda tidak melanggar aturan) dan seorang instruktur.

Aturannya adalah:

  1. Tulis persis satu tes baru, tes terkecil yang bisa Anda lakukan yang mengarah pada solusi
  2. Lihat itu gagal; kegagalan kompilasi dihitung sebagai kegagalan
  3. Buat tes dari (1) lulus dengan menulis kode implementasi paling tidak yang Anda bisa dalam metode pengujian .
  4. Refactor untuk menghilangkan duplikasi, dan jika tidak diperlukan untuk meningkatkan desain. Ketat menggunakan gerakan ini:
    1. Anda menginginkan metode baru — tunggu hingga waktu refactoring, lalu… buat metode baru (non-tes) dengan melakukan salah satu dari ini, dan tidak dengan cara lain:
      • lebih disukai: lakukan Extract Method pada kode implementasi yang dibuat sesuai dengan (3) untuk membuat metode baru di kelas tes, atau
      • jika Anda harus: memindahkan kode implementasi sesuai (3) ke metode implementasi yang ada
    2. Anda ingin kelas baru — tunggu sampai waktu refactoring, lalu ... buat kelas non-tes untuk memberikan tujuan untuk Metode Pindah dan tanpa alasan lain
    3. mengisi kelas implementasi dengan metode dengan melakukan Metode Pindah, dan tidak ada cara lain

Biasanya, ini akan mengarah pada desain yang sangat berbeda dari "metode pseudo-TDD" yang sering dipraktikkan, membayangkan di kepala Anda seperti apa desainnya, kemudian menulis tes untuk memaksa desain itu, mengimplementasikan desain yang sudah Anda bayangkan sebelum menulis Anda tes ".

Ketika sekelompok orang menerapkan sesuatu seperti permainan tic tac toe menggunakan pseudo-TDD, mereka biasanya berakhir dengan desain yang sangat mirip yang melibatkan beberapa jenis Boardkelas dengan array 3 × 3 Integers. Dan setidaknya sebagian programmer benar-benar telah menulis kelas ini tanpa tes untuk itu karena mereka "tahu bahwa mereka akan membutuhkannya" atau "perlu sesuatu untuk menulis tes mereka terhadap". Namun, ketika Anda memaksa kelompok yang sama untuk menerapkan TDD As If You Meant It, mereka akan sering berakhir dengan keragaman desain yang sangat berbeda, seringkali tidak menggunakan apa pun yang bahkan mirip dengan a Board.

Apakah ada cara Anda dapat mengidentifikasi apakah tes yang Anda tulis sudah cukup?

Ketika mereka memenuhi semua persyaratan bisnis. Tes adalah penyandian persyaratan sistem.

Apakah praktik yang baik untuk menulis tes untuk fungsionalitas yang sangat sederhana yang mungkin setara dengan 1 + 1 = 2 atau hanya overplay?

Sekali lagi, Anda memilikinya mundur: Anda tidak menulis tes untuk fungsionalitas. Anda menulis fungsionalitas untuk tes. Jika fungsionalitas untuk lulus tes ternyata sepele, itu hebat! Anda baru saja memenuhi persyaratan sistem dan bahkan tidak perlu bekerja keras untuk itu!

Apakah baik untuk mengubah fungsionalitas dan menguji apakah persyaratan berubah?

Tidak. Sebaliknya. Jika persyaratan berubah, Anda mengubah tes yang sesuai dengan persyaratan itu, melihatnya gagal, lalu mengubah kode agar lulus. Tes selalu diutamakan.

Sulit untuk melakukan ini. Anda perlu lusinan, mungkin ratusan jam latihan yang disengaja untuk membangun semacam "memori otot" untuk mencapai suatu titik, di mana ketika tenggat waktu tampak dan Anda berada di bawah tekanan, Anda bahkan tidak perlu memikirkannya , dan melakukan ini menjadi cara tercepat dan paling alami untuk bekerja.

Jörg W Mittag
sumber
1
Jawaban yang sangat jelas! Dari perspektif praktis, kerangka pengujian yang fleksibel dan kuat sangat menyenangkan ketika berlatih TDD. Meskipun tidak tergantung pada TDD, kemampuan untuk menjalankan tes secara otomatis sangat berharga untuk men-debug aplikasi. Untuk memulai dengan TDD, program non-interarctive (UNIX-style) mungkin yang paling mudah, karena use-case dapat diuji dengan membandingkan status keluar dan output dari program dengan apa yang diharapkan. Contoh nyata dari pendekatan ini dapat ditemukan di perpustakaan Bensin saya untuk OCaml.
Michael Le Barbier Grünewald
4
Anda mengatakan "ketika Anda memaksa kelompok yang sama untuk menerapkan TDD. Seolah-olah Anda menginginkannya, mereka akan sering berakhir dengan beragam desain yang sangat berbeda, seringkali tidak menggunakan apa pun yang bahkan mirip dengan Dewan" seolah-olah itu adalah hal yang baik. . Tidak jelas sama sekali bagi saya bahwa itu adalah hal yang baik, dan bahkan mungkin buruk dari sudut pandang pemeliharaan karena kedengarannya seperti implementasi akan sangat kontra-intuitif untuk seseorang yang baru. Bisakah Anda menjelaskan mengapa keragaman implementasi ini adalah hal yang baik, atau setidaknya tidak buruk?
Jim Clay
3
+1 Jawabannya bagus karena menjelaskan TDD dengan benar. Namun, ini juga menunjukkan mengapa TDD adalah metodologi yang cacat: pemikiran yang cermat dan desain eksplisit diperlukan, terutama ketika dihadapkan dengan masalah algoritmik. Melakukan TDD "in the blind" (seperti yang ditentukan TDD) dengan berpura-pura tidak memiliki pengetahuan domain menyebabkan kesulitan yang tidak perlu dan jalan buntu. Lihat bencana solver Sudoku yang terkenal buruk (versi pendek: TDD tidak bisa mengalahkan pengetahuan domain).
Andres F.
1
@AndresF .: Sebenarnya, postingan blog yang Anda tautkan tampaknya menggemakan pengalaman yang dibuat Keith ketika melakukan TDD As If You Meant It: ketika melakukan "pseudo-TDD" untuk Tic-Tac-Toe, mereka mulai dengan membuat Boardkelas dengan 3x3 array ints (atau sesuatu seperti itu). Sedangkan, jika Anda memaksa mereka untuk melakukan TDDAIYMI, mereka sering membuat mini-DSL untuk menangkap pengetahuan domain. Itu hanya anekdot, tentu saja. Sebuah studi yang baik secara statistik dan ilmiah akan bagus, tetapi seperti yang sering terjadi dengan studi seperti ini, mereka terlalu kecil atau terlalu mahal.
Jörg W Mittag
@ JörgWMittag Perbaiki saya jika saya salah paham dengan Anda, tetapi apakah Anda mengatakan bahwa Ron Jeffries sedang melakukan "pseudo-TDD"? Bukankah itu bentuk kekeliruan "no true Scotsman"? (Saya setuju dengan Anda tentang perlunya lebih banyak studi ilmiah; blog yang saya tautkan hanya sebuah anekdot penuh warna tentang kegagalan spektakuler dari contoh spesifik penggunaan TDD. Sayangnya, sepertinya penginjil TDD terlalu keras untuk sisanya. dari kita untuk memiliki analisis nyata dari metholody ini dan manfaat yang diduga).
Andres F.
5

Anda menggambarkan pendekatan pengembangan Anda sebagai proses "top-down-only" - Anda mulai dari tingkat abstraksi yang lebih tinggi dan semakin detail. TDD, setidaknya dalam bentuk seperti yang populer, adalah teknik "bottom-up". Dan bagi seseorang yang bekerja sebagian besar "top-down" memang bisa sangat tidak biasa untuk bekerja "bottom-up".

Jadi, bagaimana Anda bisa membawa lebih banyak "TDD" ke dalam proses pengembangan Anda? Pertama, saya menganggap proses pengembangan Anda yang sebenarnya tidak selalu "top-down" seperti yang Anda gambarkan di atas. Setelah langkah 2, Anda mungkin akan mengidentifikasi beberapa komponen yang independen dari komponen lain. Terkadang Anda memutuskan untuk mengimplementasikan komponen-komponen itu terlebih dahulu. Detail API publik dari komponen-komponen itu mungkin tidak hanya mengikuti kebutuhan Anda, detailnya juga mengikuti keputusan desain Anda. Ini adalah titik di mana Anda bisa mulai dengan TDD: bayangkan bagaimana Anda akan menggunakan komponen, dan bagaimana Anda benar-benar akan menggunakan API. Dan ketika Anda mulai mengkode penggunaan API seperti itu dalam bentuk tes, Anda baru saja mulai dengan TDD.

Kedua, Anda dapat melakukan TDD bahkan ketika Anda akan lebih banyak kode "top-down", dimulai dengan komponen yang tergantung pada komponen lain yang tidak ada terlebih dahulu. Yang harus Anda pelajari adalah bagaimana "mengejek" semua dependensi ini terlebih dahulu. Itu akan memungkinkan Anda untuk membuat dan menguji komponen tingkat tinggi sebelum pergi ke komponen tingkat bawah. Contoh yang sangat rinci tentang melakukan TDD dengan cara top-down dapat ditemukan di posting blog Ralf Westphal ini .

Doc Brown
sumber
3

Apakah saya melakukan itu benar? Kalau bukan apa tepatnya aku harus berubah

Anda baik-baik saja.

Apakah ada cara Anda dapat mengidentifikasi apakah tes yang Anda tulis sudah cukup?

Ya, gunakan alat cakupan tes / kode . Martin Fowler menawarkan beberapa saran bagus tentang cakupan tes.

Apakah praktik yang baik untuk menulis tes untuk fungsionalitas yang sangat sederhana yang mungkin setara dengan 1 + 1 = 2 atau hanya overplay?

Secara umum, setiap fungsi, metode, komponen dll yang Anda harapkan untuk menghasilkan beberapa hasil mengingat beberapa input adalah kandidat yang baik untuk pengujian unit. Namun, seperti kebanyakan hal dalam kehidupan (teknik), Anda perlu mempertimbangkan pertukaran: Apakah upaya tersebut diimbangi dengan menulis uji unit yang menghasilkan basis kode yang lebih stabil dalam jangka panjang? Secara umum, pilih untuk menulis kode uji untuk fungsi penting / kritis terlebih dahulu. Kemudian jika Anda menemukan ada bug yang terkait dengan beberapa bagian kode yang belum diuji, tambahkan lebih banyak tes.

Apakah baik untuk mengubah fungsionalitas dan menguji apakah persyaratan berubah?

Hal yang baik tentang memiliki tes otomatis adalah bahwa Anda akan segera melihat apakah perubahan melanggar pernyataan sebelumnya. Jika Anda mengharapkan ini karena persyaratan yang diubah, ya tidak apa-apa untuk mengubah kode tes (pada kenyataannya, dalam TDD murni Anda akan mengubah tes terlebih dahulu sesuai dengan persyaratan, kemudian mengadopsi kode sampai memenuhi persyaratan baru).

miraculixx
sumber
Cakupan kode mungkin bukan ukuran yang sangat andal. Menegakkan% cakupan biasanya menghasilkan banyak tes yang tidak perlu (seperti tes untuk semua parameter pemeriksaan nol, dll. - yang merupakan tes demi tes yang menambah nilai hampir tidak ada) dan membuang waktu pengembangan, sementara sulit untuk menguji kode jalur mungkin tidak diuji sama sekali.
Paul
3

Tes menulis pertama adalah pendekatan yang sama sekali berbeda untuk menulis perangkat lunak. Tes tidak hanya alat verifikasi fungsionalitas kode yang tepat (semuanya lulus) tetapi kekuatan yang menentukan desain. Meskipun cakupan pengujian mungkin merupakan metrik yang bermanfaat, itu tidak harus menjadi tujuan itu sendiri - tujuan TDD bukan untuk mendapatkan% cakupan kode yang baik, tetapi untuk memikirkan tentang kemampuan pengujian kode Anda sebelum menulisnya.

Jika Anda memiliki masalah dengan tes menulis pertama, saya akan sangat menyarankan melakukan sesi pemrograman pasangan dengan seseorang yang berpengalaman dalam TDD, sehingga Anda mendapatkan pengalaman "cara berpikir" tentang seluruh pendekatan.

Hal lain yang baik untuk dilakukan adalah menonton video online di mana perangkat lunak sedang dikembangkan menggunakan TDD dari baris pertama. Salah satu yang baik yang pernah saya gunakan untuk memperkenalkan diri pada TDD adalah Let's Play TDD oleh James Shore. Lihatlah, itu akan menggambarkan bagaimana desain muncul bekerja, pertanyaan apa yang harus Anda tanyakan pada diri Anda sendiri saat menulis tes dan bagaimana kelas dan metode baru dibuat, di refactored dan diulangi.

Apakah ada cara Anda dapat mengidentifikasi apakah tes yang Anda tulis sudah cukup?

Saya percaya ini adalah pertanyaan yang salah untuk ditanyakan. Ketika Anda melakukan TDD, Anda memilih untuk melakukan TDD dan desain yang muncul sebagai cara untuk menulis perangkat lunak. Jika ada fungsionalitas baru yang Anda perlu tambahkan selalu dimulai dengan tes, itu akan selalu ada.

Apakah praktik yang baik untuk menulis tes untuk fungsionalitas yang sangat sederhana yang mungkin setara dengan 1 + 1 = 2 atau hanya overplay?

Jelas itu tergantung, gunakan penilaian Anda. Saya lebih suka untuk tidak menulis tes pada parameter cek nol, jika metode ini bukan bagian dari API publik, tetapi jika tidak, mengapa Anda tidak mengonfirmasi bahwa metode Tambahkan (a, b) mengembalikan a + b memang?

Apakah baik untuk mengubah fungsionalitas dan menguji apakah persyaratan berubah?

Sekali lagi, ketika Anda mengubah atau menambahkan fungsionalitas baru ke kode Anda, Anda mulai dengan tes, apakah itu menambah tes baru atau mengubah yang sudah ada ketika persyaratan berubah.

Paul
sumber