Mengapa TDD berfungsi? [Tutup]

92

Pengembangan yang digerakkan oleh tes (TDD) besar hari ini. Saya sering melihatnya direkomendasikan sebagai solusi untuk berbagai masalah di sini di Programmer SE dan tempat lainnya. Saya bertanya-tanya mengapa ini berhasil.

Dari sudut pandang teknik, saya bingung karena dua alasan:

  1. Pendekatan "tulis tes + refactor sampai lulus" terlihat sangat anti-rekayasa. Jika insinyur sipil menggunakan pendekatan itu untuk pembangunan jembatan, atau perancang mobil untuk mobil mereka, misalnya, mereka akan membentuk kembali jembatan atau mobil mereka dengan biaya yang sangat tinggi, dan hasilnya akan menjadi kekacauan yang diperbaiki tanpa arsitektur yang dipikirkan dengan matang. . Pedoman "refactor till pass" sering diambil sebagai mandat untuk melupakan desain arsitektur dan melakukan apa pun yang diperlukan untuk mematuhi tes; dengan kata lain, tes, bukan pengguna, yang menetapkan persyaratan. Dalam situasi ini, bagaimana kita dapat menjamin "kemampuan" yang baik dalam hasil, yaitu hasil akhir yang tidak hanya benar tetapi juga dapat diperluas, kuat, mudah digunakan, andal, aman, aman, dll.? Inilah yang biasanya dilakukan arsitektur.
  2. Pengujian tidak dapat menjamin bahwa suatu sistem berfungsi; itu hanya dapat menunjukkan bahwa itu tidak. Dengan kata lain, pengujian dapat menunjukkan kepada Anda bahwa suatu sistem mengandung cacat jika gagal dalam suatu pengujian, tetapi suatu sistem yang lulus semua pengujian tidak lebih aman daripada sistem yang gagal. Cakupan tes, kualitas tes, dan faktor-faktor lain sangat penting di sini. Perasaan aman palsu yang dihasilkan oleh "semua hijau" hasil bagi banyak orang telah dilaporkan dalam industri sipil dan kedirgantaraan sebagai sangat berbahaya, karena mungkin ditafsirkan sebagai "sistem baik-baik saja", ketika itu benar-benar berarti "sistem sama baiknya" sebagai strategi pengujian kami ". Seringkali, strategi pengujian tidak dicentang. Atau, siapa yang menguji tes?

Singkatnya, saya lebih peduli tentang bit "drive" dalam TDD daripada tentang bit "test". Pengujian sepenuhnya OK; yang tidak saya dapatkan adalah menggerakkan desain dengan melakukannya.

Saya ingin melihat jawaban yang berisi alasan mengapa TDD dalam rekayasa perangkat lunak adalah praktik yang baik, dan mengapa masalah yang saya jelaskan di atas tidak relevan (atau tidak cukup relevan) dalam kasus perangkat lunak. Terima kasih.

CesarGon
sumber
53
Jembatan, mobil, dan desain fisik lainnya jauh dari lunak seperti perangkat lunak. Ini adalah perbedaan penting, dan berarti bahwa perbandingan antara perangkat lunak dan rekayasa nyata tidak selalu relevan. Apa yang berfungsi untuk jembatan mungkin tidak berfungsi untuk perangkat lunak, dan sebaliknya.
Lars Wirzenius
9
Saya agak setuju dengan keraguan Anda. Sebagai contoh saya telah mengakui saya mendapat kesan bahwa memiliki test suite dapat memiliki efek samping yang entah bagaimana "melunak" perhatian ketika menulis kode. Tentu saja tes adalah hal yang baik (yang wajib jika Anda ingin memiliki kemungkinan untuk refactor), tetapi hanya jika mereka menambah perhatian pada detail, kasus perbatasan, efisiensi atau ekstensibilitas dan tidak jika mereka menggantinya.
6502
2
@ 6502: Bagaimanapun juga! TDD bukan peluru perak dan tidak akan menyelesaikan semua masalah yang muncul selama pengembangan perangkat lunak. Namun ini adalah metode yang berguna untuk mengatur alur kerja. Misalnya, Anda dapat memberlakukan persyaratan bahwa semua kasus perbatasan dicakup oleh pengujian. Anda masih perlu tahu apa ini kasus perbatasan, tetapi sekarang Anda juga memiliki alat untuk memeriksa apakah kode Anda berurusan dengan mereka dengan benar.
Mchl
2
@ CesarGon, Anda mungkin juga tertarik pada pertanyaan ini yang saya tanyakan pada SO beberapa waktu lalu ... Tidak cukup TDD, tetapi terkait ... Beberapa jawaban yang sangat mencerahkan di sana.
AviD
6
Betapa menakjubkan bahwa analog teknik sipil / pengembangan perangkat lunak tidak bertahan. Sepanjang baris yang sama, saya sering memperhatikan bahwa saya tidak bisa memasak pancake dengan cara yang sama seperti saya memotong rumput saya.

Jawaban:

66

Saya pikir ada satu kesalahpahaman di sini. Dalam desain perangkat lunak, desainnya sangat dekat dengan produk. Dalam teknik sipil, arsitektur, desain dipisahkan dari produk yang sebenarnya: ada cetak biru yang menahan desain, yang kemudian terwujud menjadi produk jadi, dan mereka dipisahkan oleh sejumlah besar waktu dan usaha.

TDD sedang menguji desain. Tetapi setiap desain mobil dan desain bangunan juga diuji. Teknik konstruksi pertama kali dihitung, kemudian diuji dalam skala yang lebih kecil, kemudian diuji dalam skala yang lebih besar, sebelum dimasukkan ke dalam bangunan nyata. Ketika mereka menemukan balok-H dan beban misalnya, yakinlah bahwa ini telah dicoba dan dicoba lagi sebelum mereka benar-benar membangun jembatan pertama dengannya.

Desain mobil juga diuji, dengan merancang prototipe, dan ya, tentu dengan menyesuaikan hal-hal yang tidak sepenuhnya benar, hingga memenuhi harapan. Namun, bagian dari proses ini lebih lambat, karena seperti yang Anda katakan, Anda tidak dapat dipusingkan dengan produk tersebut. Tetapi setiap desain ulang mobil mengacu pada pengalaman yang dipelajari dari yang sebelumnya, dan setiap bangunan memiliki sekitar fundamental dasar selama ribuan tahun di belakangnya tentang pentingnya ruang, cahaya, isolasi, kekuatan, dll. Rincian diubah dan ditingkatkan, baik di gedung-gedung dan dalam mendesain ulang untuk yang lebih baru.

Juga, bagian diuji. Mungkin tidak persis dalam gaya yang sama dengan perangkat lunak, tetapi bagian-bagian mekanik (roda, penyala, kabel) biasanya diukur dan ditekan untuk mengetahui ukurannya benar, tidak ada kelainan yang terlihat, dll. Mereka mungkin xrayed atau laser- diukur, mereka mengetuk batu bata untuk menemukan yang rusak, mereka mungkin benar-benar diuji dalam beberapa konfigurasi atau lainnya, atau mereka menggambar representasi terbatas dari kelompok besar untuk benar-benar mengujinya.

Itu semua adalah hal yang dapat Anda lakukan dengan TDD.

Dan memang, pengujian bukanlah jaminan. Program macet, mobil mogok, dan bangunan mulai melakukan hal-hal lucu ketika angin bertiup. Tapi ... 'keamanan' bukan pertanyaan boolean. Bahkan ketika Anda tidak bisa memasukkan semuanya, mampu meliput - katakanlah - 99% kemungkinan lebih baik daripada hanya mencakup 50%. Tidak menguji dan kemudian menemukan bahwa baja belum beres dengan baik dan rapuh dan pecah pada pukulan pertama dari palu ketika Anda hanya memasang struktur utama Anda adalah buang-buang uang. Bahwa ada kekhawatiran lain yang mungkin masih menyakiti bangunan tidak membuatnya kurang bodoh untuk membiarkan cacat yang mudah dicegah menjatuhkan desain Anda.

Mengenai praktik TDD, itu adalah masalah keseimbangan. Biaya melakukannya satu arah (misalnya, tidak menguji, dan kemudian mengambil potongan nanti), dibandingkan biaya melakukannya dengan cara lain. Itu selalu keseimbangan. Tetapi jangan berpikir bahwa proses desain lainnya tidak memiliki pengujian dan TDD di tempat.

Inca
sumber
7
+1 untuk berbicara tentang di mana pengujian terjadi di manufaktur. Poin yang bagus.
Adam Lear
11
Anda mengatakan "bagian diuji". Tentu, tetapi tidak dirancang berdasarkan uji coba. Bagian pesawat terbang tidak dirancang dengan cara uji-didorong, tetapi dengan cara arsitektur, desain-depan besar. Kesamaan dengan TDD tidak ada di sini.
CesarGon
3
Menambah itu: TDD, menurut pendapat saya, terutama tentang cara untuk memastikan Anda dapat memeriksa bagian daripada 'semua atau tidak sama sekali' pada akhirnya. Tetapi adagium dari TDD, 'bangun tes dulu' tidak dimaksudkan sebagai 'buat tes sebelum Anda berpikir tentang apa yang ingin Anda capai'. Karena memikirkan tes adalah bagian dari mendesain. Menentukan bagian yang ingin Anda lakukan, mendesain. Sebelum Anda mulai mengetik, Anda telah melakukan beberapa desain. (Dengan cara itu saya pikir istilah 'test driven design' secara menyesatkan menyiratkan jalur oneway, di mana itu benar-benar merupakan lingkaran umpan balik).
Inca
2
+1: Perangkat lunak murni desain. Analogi jembatan dalam pertanyaan itu sepenuhnya salah. TDD sepenuhnya menerapkan pengujian unit luar. Desain Test-Driven berlaku di semua lapisan desain.
S.Lott
3
@CesarGon: Tidak, TDD mengendalikan PERKEMBANGAN dengan menguji. Itu berbeda dengan mengendarai desain. Desain menentukan bagaimana Anda akan menggunakan sistem, dan dengan demikian tes mana yang perlu Anda terapkan untuk mereplikasi perilaku itu. Menerapkan tes-tes itu sering membantu Anda memperbaiki desain.
deworde
26

IMO, sebagian besar kisah sukses untuk TDD adalah palsu dan hanya untuk tujuan pemasaran. Mungkin ada sedikit keberhasilan dengan itu, tetapi hanya untuk aplikasi kecil. Saya sedang mengerjakan aplikasi silverlight besar di mana prinsip-prinsip TDD digunakan. Aplikasi sudah mendapat ratusan tes tetapi masih belum stabil. Beberapa bagian aplikasi tidak dapat diuji karena interaksi pengguna yang kompleks. Menghasilkan tes dengan banyak ejekan dan sulit untuk memahami kode.

Awalnya ketika kami mencoba TDD, semuanya tampak baik. Saya bisa menulis banyak tes dan mengejek bagian-bagian yang sulit untuk unit test. Setelah Anda memiliki cukup banyak kode dan perubahan antarmuka diperlukan, Anda dikacaukan. Banyak tes yang harus diperbaiki dan Anda akan menulis ulang lebih banyak tes daripada perubahan kode yang sebenarnya.

Peter Norvig Menjelaskan pandangannya tentang TDD dalam buku Coders At Work.

Seibel: Bagaimana dengan gagasan menggunakan tes untuk mendorong desain?

Norvig: Saya melihat tes lebih sebagai cara untuk memperbaiki kesalahan daripada sebagai cara desain. Pendekatan ekstrem ini mengatakan, "Ya, hal pertama yang Anda lakukan adalah menulis tes yang mengatakan saya mendapatkan jawaban yang benar di akhir," dan kemudian Anda menjalankannya dan melihat bahwa itu gagal, dan kemudian Anda berkata, "Apa yang saya lakukan perlu berikutnya? ”- itu sepertinya bukan cara yang tepat untuk merancang sesuatu untuk saya. Sepertinya hanya jika itu sangat sederhana sehingga solusinya sudah ditakdirkan akan masuk akal. Saya pikir Anda harus memikirkannya terlebih dahulu. Anda harus mengatakan, “Apa saja bagiannya? Bagaimana saya bisa menulis tes untuk potongan-potongan sampai saya tahu beberapa dari mereka? ”Dan kemudian, setelah Anda selesai melakukannya, maka itu adalah disiplin yang baik untuk memiliki tes untuk masing-masing bagian dan untuk memahami dengan baik bagaimana mereka berinteraksi satu sama lain dan kasus batas dan sebagainya. Mereka semua harus menjalani tes. Tapi saya tidak berpikir Anda menggerakkan seluruh desain dengan mengatakan, "Tes ini telah gagal."

Navaneeth KN
sumber
7
Sekarang, jika Anda memberi tahu fakta-fakta ini kepada orang-orang dan konsultan TDD, jawaban yang Anda dapatkan adalah,well, you haven't done TDD right!
Navaneeth KN
10
Dan mereka benar. Kami sedang melakukan BDD / TDD pada sistem volume yang sangat tinggi, dan sudah bekerja dengan baik. Tes ada di sana untuk memberi tahu Anda bahwa Anda melanggar perilaku yang diharapkan. Jika Anda pergi dan mengubahnya nanti "melanggar" tes, Anda sebenarnya melakukan kesalahan. Tes harus diubah terlebih dahulu untuk memperkuat perilaku BARU sistem, lalu Anda mengubahnya. Dan ya, jika Anda melakukannya dengan benar, Anda menulis tes Anda mulai dengan "apa yang perlu dilakukan hal ini" dan proses penulisan tes membantu Anda berpikir "apa yang perlu dilakukan TI untuk melakukan tugasnya." Oh, dan tidak ada konsultan yang pernah digunakan ...
Andy
4
Melakukan banyak tes tidak membebaskan Anda dari membuat desain yang tepat. Desain yang sangat berpasangan terlepas dari berapa banyak tes yang dibangun di sekitarnya dan akan selalu rapuh. tes embedding dalam desain ini bisa sangat baik membuat semuanya menjadi lebih buruk.
Newtopian
3
Ini bukan masalah melakukan kesalahan atau menjadi desain yang sangat berpasangan. Faktanya adalah bahwa antarmuka berubah. Itu berarti semua tes yang menggunakan antarmuka itu harus berubah. Pada sistem besar menjaga pengujian tetap sinkron dengan perubahan yang diperlukan dapat mulai membebani implementasi. Ini menjadi masalah yang lebih besar jika Anda melakukan pengembangan tangkas karena kemungkinan perubahan antarmuka jauh lebih mungkin. Adalah lucu bagaimana ketika metodologi tidak bekerja, pendukung metodologi bersikeras Anda melakukan kesalahan. Kemungkinan besar metodologi ini tidak cocok untuk semua domain masalah.
Dunk
2
Dalam pengalaman saya melakukan TDD berfungsi untuk aplikasi atau modul kecil. Ketika saya harus mengerjakan sesuatu yang kompleks, TDD memperlambat saya karena memaksa saya untuk menulis spesifikasi (runnable) yang terperinci sebelum saya mendapatkan gambaran keseluruhan yang jelas dalam pikiran saya: jadi saya tersesat dalam detail terlalu dini, dan seringkali saya harus membuang sejumlah tes jika saya tahu bahwa saya tidak perlu kelas tertentu (saya masih bermain dengan desain). Dalam kasus seperti itu saya lebih memilih untuk mendapatkan desain keseluruhan yang masuk akal terlebih dahulu, dan kemudian menyempurnakan rincian implementasi (mungkin menggunakan TDD).
Giorgio
25

Design Driven Design berfungsi untuk saya karena alasan berikut:

Ini adalah bentuk spesifikasi runnable.

Ini berarti Anda dapat melihat dari test case:

  1. BAHWA kode yang dipanggil memenuhi spesifikasi sebagaimana hasil yang diharapkan ada di sana dalam kasus uji. Inspeksi visual (yang mengharapkan kasus uji untuk lulus) dapat mengatakan langsung "oh, tes ini memeriksa bahwa perusahaan faktur panggilan mengingat situasi ini, harus memiliki hasil ITU".
  2. BAGAIMANA kode harus dipanggil. Langkah-langkah aktual yang diperlukan untuk melakukan tes ditentukan secara langsung tanpa perancah eksternal (basis data dicemooh dll).

Anda yang menulis tampilan dari luar terlebih dahulu.

Kode yang sering ditulis dengan cara di mana Anda pertama kali memecahkan masalah, dan kemudian Anda memikirkan bagaimana kode yang baru saja Anda tulis dipanggil. Ini sering memberikan antarmuka yang canggung karena seringkali lebih mudah untuk "hanya menambahkan bendera" dll. Dengan berpikir "kita perlu melakukan INI sehingga testcases akan terlihat seperti ITU" di depan Anda membalikkan ini. Ini akan memberikan modularitas yang lebih baik, karena kode akan ditulis sesuai dengan antarmuka panggilan, bukan sebaliknya.

Ini biasanya akan menghasilkan kode pembersih juga yang memerlukan dokumentasi yang kurang jelas.

Anda selesai lebih cepat

Karena Anda memiliki spesifikasi pada formulir runnable, Anda selesai ketika suite tes lengkap berlalu. Anda dapat menambahkan lebih banyak tes saat Anda mengklarifikasi hal-hal pada tingkat yang lebih rinci, tetapi sebagai prinsip dasar Anda memiliki indikator kemajuan yang sangat jelas dan terlihat dan ketika Anda selesai.

Ini berarti bahwa Anda dapat mengetahui kapan pekerjaan itu perlu atau tidak (apakah itu membantu lulus ujian) Anda akhirnya perlu melakukan lebih sedikit.

Bagi mereka yang merenungkannya mungkin berguna bagi mereka, saya mendorong Anda untuk menggunakan TDD untuk rutin perpustakaan Anda berikutnya. Perlahan atur spesifikasi yang bisa dijalankan, dan buat kode lulus tes. Setelah selesai, spesifikasi runnable tersedia untuk semua orang yang perlu melihat cara menjalankan perpustakaan.

Penelitian baru-baru ini

"Hasil studi kasus menunjukkan bahwa kepadatan cacat pra-rilis dari empat produk menurun antara 40% dan 90% relatif terhadap proyek serupa yang tidak menggunakan praktik TDD. Subyektif, tim mengalami peningkatan 15 hingga 35% pada waktu pengembangan awal setelah mengadopsi TDD. " ~ Hasil dan Pengalaman 4 Tim Industri

Dave Jarvis
sumber
5
Saya ingin menambahkan ini bahwa Anda sebenarnya MEMILIKI pedoman yang agak masuk akal dan jelas tentang kapan Anda selesai. Tanpa prosedur yang jelas untuk memverifikasi secara objektif bahwa Anda telah menyelesaikan tugas yang ada, itu sulit untuk diketahui. Pengalaman saya sendiri termasuk berjam-jam dan berhari-hari menyia-nyiakan "bernegosiasi" apakah tugas telah selesai dan terus menerus, garis bergerak. Ini berdampak pada semua tingkatan manajemen proyek termasuk penjadwalan, untuk bagaimana Anda bisa menjadwalkan tugas seperti itu? Target yang lebih jelas dengan peningkatan throughput dan komunikasi yang lebih cepat.
Edward Strange
Ini harus menjadi jawaban yang diterima.
Niing
19

Proses pembuatan perangkat lunak bukanlah proses penulisan kode. Tidak ada proyek perangkat lunak yang harus dimulai tanpa rencana 'cakupan luas' terlebih dahulu. Sama seperti proyek menjembatani dua tepi sungai, perlu rencana seperti itu terlebih dahulu.

Pendekatan TDD berhubungan (sebagian besar) dengan pengujian unit - setidaknya itulah cara orang cenderung memikirkannya - yaitu menciptakan bit kode perangkat lunak tingkat paling rendah. Ketika semua fitur dan perilaku telah didefinisikan dan kita benar-benar tahu apa yang ingin kita capai.

Dalam rekayasa struktural terlihat seperti ini:

'Kami memiliki dua keping logam yang dihubungkan bersama, dan sambungan perlu menopang gaya geser dalam urutan x. Mari kita coba metode koneksi mana yang terbaik untuk melakukan ini '

Untuk menguji apakah perangkat lunak bekerja secara keseluruhan, kami merancang jenis tes lain seperti tes kegunaan, tes integrasi dan tes penerimaan. Ini juga harus didefinisikan sebelum pekerjaan yang sebenarnya pada penulisan kode dimulai, dan dilakukan setelah unit test berwarna hijau.

Lihat V-Model: http://en.wikipedia.org/wiki/V-Model_%28software_development%29

Mari kita lihat bagaimana cara kerjanya untuk jembatan:

  1. Pemerintah setempat mengatakan kepada perusahaan pembuat jembatan: "Kami membutuhkan jembatan untuk menghubungkan kedua titik ini. Jembatan harus dapat memungkinkan jumlah lalu lintas per jam dan siap untuk 21 Desember 2012 '- ini adalah definisi dari tes penerimaan. Perusahaan tidak akan mendapatkan jumlah penuh (atau apa pun) uang, jika mereka tidak dapat lulus tes itu.

  2. Manajemen perusahaan memutuskan jadwal proyek. Mereka membentuk tim kerja dan menetapkan tujuan untuk setiap tim. Jika tim tidak akan mencapai tujuan ini - jembatan tidak akan dibangun tepat waktu. Namun - ada beberapa tingkat fleksibilitas di sini. Jika salah satu tim memiliki beberapa masalah, perusahaan dapat mengimbanginya dengan mengubah persyaratan, mengubah subkontraktor, merekrut lebih banyak orang, dll. Sehingga seluruh proyek masih memenuhi tujuan yang ditetapkan pada poin # 1.

  3. Di dalam tim yang bertanggung jawab untuk merancang komponen jembatan tertentu seperti pada contoh yang saya berikan di atas. Terkadang solusinya jelas, karena kami memiliki banyak pengetahuan tentang membangun jembatan (seperti menggunakan perpustakaan yang teruji dengan baik dalam pengembangan perangkat lunak - Anda hanya menganggap itu berfungsi seperti yang diiklankan). Terkadang Anda perlu membuat beberapa desain dan mengujinya untuk memilih yang terbaik. Namun, kriteria pengujian komponen diketahui sebelumnya.

Mchl
sumber
Jika saya memahami Anda dengan benar, Anda mengatakan bahwa TDD baik-baik saja selama (a) digunakan untuk pengujian unit saja, dan (b) disertai dengan pendekatan pengujian lain juga. Jika ini masalahnya, ini dapat membahas poin 2 di OP. Bagaimana Anda mengatasi poin nomor 1?
CesarGon
@CesarGon: TDD juga berfungsi bagus untuk tes integrasi.
sevenseacat
Poin 1 bermuara pada tindakan itu, bahwa sebelum proyek akhir untuk mobil atau jembatan diterima, ia melewati banyak pengulangan di mana semua detailnya ditinjau dan diuji terhadap persyaratan yang diberlakukan oleh 'rencana cakupan luas'. Ini sebagian besar dilakukan di atas kertas (atau dalam memori komputer), karena itu lebih murah dalam hal ini, tetapi perhatikan bahwa sering kali ada prototipe fisik yang dibuat dari kedua keseluruhan konstruksi (mungkin bukan dalam rangka jembatan) serta komponen-komponennya.
Mchl
@Karpie: Dan untuk tes penerimaan juga! Anda harus tahu sebelumnya apa yang dibutuhkan agar pekerjaan Anda diterima oleh klien.
Mchl
1
Baiklah kalau begitu. Cukup banyak tim pertama yang memulai pekerjaan adalah tim arsitek yang diperintahkan untuk merancang jembatan yang mampu memenuhi kriteria klien, sementara juga murah dan mungkin terlihat bagus dan tidak akan jatuh pada hembusan angin pertama yang lebih kuat. Tim mungkin mengusulkan beberapa desain kasar kurang lebih memenuhi kriteria ini, kemudian pilih satu dan mengerjakannya secara lebih rinci, ulangi, ulangi, ulangi sampai desain siap (yaitu memenuhi kriteria yang diberikan, dan cukup rinci sehingga tahap lain dari proyek dapat dimulai)
Mchl
18

Dalam pikiran saya TDD berfungsi karena

  • Ini memaksa Anda menentukan apa yang Anda ingin unit lakukan sebelum memutuskan implementasi pada tingkat presisi yang umumnya tidak tercakup oleh dokumen spesifikasi atau persyaratan apa pun.
  • Itu membuat kode Anda secara inheren dapat digunakan kembali, karena Anda harus menggunakannya dalam skenario pengujian dan produksi
  • Ini mendorong Anda untuk menulis kode dalam eaiser yang lebih kecil untuk menguji potongan yang cenderung mengarah ke desain yang lebih baik

Khususnya pada poin yang Anda angkat

  • Kode lebih mudah dibentuk daripada bata atau baja, jadi lebih murah untuk dimodifikasi. Masih lebih murah jika Anda memiliki tes untuk memastikan perilaku tidak berubah
  • TDD bukan alasan untuk tidak melakukan desain - arsitektur tingkat tinggi secara umum masih disarankan, hanya saja tidak dengan terlalu banyak detail. Desain Front Besar tidak dianjurkan, tetapi melakukan desain yang cukup dianjurkan
  • TDD tidak dapat menjamin suatu sistem berfungsi, tetapi hal itu mencegah banyak kesalahan kecil yang melewatinya jika tidak akan terlewatkan. Juga karena umumnya mendorong kode dengan faktor yang lebih baik, sering kali lebih mudah dipahami sehingga kecil kemungkinannya untuk menjadi buggy
Gavin Clarke
sumber
3
Anda juga harus menambahkan bahwa ketika ditemukan cacat, Anda dapat memastikan bahwa mereka tidak akan terulang karena Anda akan menambahkan tes lain.
Andy
16

TL; DR

Pemrograman masih merupakan kegiatan desain, ini bukan konstruksi. Unit penulisan menguji setelah fakta hanya mengkonfirmasi bahwa kode melakukan apa yang dilakukannya, bukan bahwa ia melakukan sesuatu yang bermanfaat. Kegagalan tes adalah nilai sebenarnya karena mereka membiarkan Anda menangkap kesalahan lebih awal.

Kode adalah Desain

Dalam Bab 7 PPP "Paman Bob" berbicara tentang masalah ini secara langsung. Sangat awal dalam bab ini, ia mereferensikan artikel hebat oleh Jack Reeves di mana ia mengusulkan bahwa kode itu dirancang (tautannya menuju halaman yang mengumpulkan ketiga artikelnya tentang topik tersebut).

Apa yang menarik tentang argumen ini adalah bahwa ia menunjukkan, tidak seperti disiplin teknik lainnya di mana konstruksi adalah kegiatan yang sangat mahal, konstruksi perangkat lunak relatif gratis (tekan kompilasi dalam IDE Anda dan Anda memiliki perangkat lunak yang Anda buat). Jika Anda melihat kode penulisan sebagai aktivitas desain alih-alih aktivitas konstruksi, maka siklus refactor merah-hijau pada dasarnya adalah latihan dalam desain. Desain Anda berkembang saat Anda menulis tes, kode untuk memuaskan mereka, dan refactor untuk mengintegrasikan kode baru ke dalam sistem yang ada.

TDD sebagai Spesifikasi

Tes unit yang Anda tulis untuk TDD adalah terjemahan langsung dari spesifikasi yang Anda pahami. Dengan menulis kode yang minimal memenuhi spesifikasi Anda (membuat tes Anda berubah menjadi hijau), semua kode yang Anda tulis ada di sana untuk tujuan tertentu. Apakah tujuan itu telah dipenuhi atau belum divalidasi oleh tes yang berulang.

Tulis Tes untuk Fungsionalitas

Kesalahan umum dalam pengujian unit terjadi ketika Anda menulis tes setelah kode, Anda akhirnya menguji bahwa kode melakukan apa yang dilakukannya. Dengan kata lain Anda akan melihat tes seperti ini

public class PersonTest:Test
{
   [Test]
   TestNameProperty()
   {
      var person=new Person();
      person.Name="John Doe";
      Assert.AreEqual("John Doe", person.Name);
   }
}

Walaupun saya kira kode ini bisa berguna (pastikan seseorang belum melakukan sesuatu yang cabul dengan properti sederhana). Itu tidak berfungsi untuk memvalidasi spesifikasi. Dan seperti yang Anda katakan, menulis tes semacam ini hanya membawa Anda sejauh ini.

Sementara Green is Good, Value Lies in Red saya memiliki momen "aha" pertama saya yang sebenarnya di TDD ketika saya mengalami kegagalan tes yang tidak terduga. Saya memiliki serangkaian tes yang saya miliki untuk kerangka kerja yang saya bangun. Menambahkan fitur baru, saya menulis tes untuk itu. Kemudian menulis kode untuk lulus ujian. Kompilasi, tes ... mendapat hijau pada tes baru. Tetapi juga mendapat merah pada tes lain yang saya tidak harapkan menjadi merah.

Melihat kegagalan itu, aku menghela nafas lega karena aku ragu aku akan menangkap serangga itu untuk beberapa waktu seandainya aku tidak menjalani tes itu. Dan itu adalah bug yang SANGAT jahat untuk dimiliki. Untungnya, saya memiliki tes, dan itu memberi tahu saya apa yang harus saya lakukan untuk memperbaiki bug. Tanpa tes, saya akan terus membangun sistem saya (dengan bug menginfeksi modul lain yang bergantung pada kode itu) dan pada saat bug ditemukan, itu akan menjadi tugas utama untuk memperbaikinya dengan benar.

Manfaat sebenarnya dari TDD adalah memungkinkan kita untuk membuat perubahan dengan mengabaikan sembrono. Ini seperti jaring pengaman untuk pemrograman. Pikirkan apa yang akan terjadi jika seorang seniman trapeze melakukan kesalahan dan jatuh. Dengan internet, itu adalah kesalahan yang memalukan. Tanpa, itu adalah tragedi. Dalam hal yang sama, TDD menyelamatkan Anda dari mengubah kesalahan bertulang menjadi proyek yang membunuh bencana.

Michael Brown
sumber
4
Nilai pengujian merah yang menangkap bug adalah atribut dari Unit Testing secara umum, bukan dari TDD secara khusus.
Robert Harvey
2
Anda benar tentang hal itu. Tetapi kemungkinan bahwa saya akan memiliki bug spesifik yang ditutupi dengan pengujian unit post-hoc lebih rendah.
Michael Brown
1
Bisakah Anda mendukung klaim itu dengan beberapa bukti, data, atau analisis yang kuat?
CesarGon
1
@CesarGon studi ini , sementara programer bekerja pada proyek kecil, menunjukkan bahwa pengembang menggunakan TDD menghasilkan kode dengan cakupan tes yang lebih baik daripada pengujian setelah-fakta (92% -98% vs 80% -90%) dan akibatnya menangkap lebih banyak cacat selama pengembangan (cacat 18% lebih sedikit ditemukan dalam kode yang diproduksi menggunakan TDD).
Jules
11

Anda tidak akan menemukan siapa pun yang mendukung Pengembangan yang Didorong Tes, atau bahkan Desain yang Didorong Uji Coba (mereka berbeda), yang mengatakan tes membuktikan aplikasi. Jadi mari kita sebut saja pria jerami dan dilakukan.

Anda tidak akan menemukan orang yang tidak suka atau tidak terkesan oleh TDD yang mengatakan bahwa Tes adalah pemborosan waktu dan usaha. Meskipun tes tidak membuktikan aplikasi, mereka cukup membantu dalam menemukan kesalahan.

Dengan kedua hal tersebut, tidak ada pihak yang melakukan sesuatu yang berbeda sehubungan dengan benar-benar melakukan tes pada perangkat lunak. Keduanya sedang melakukan pengujian. Keduanya MENDAPATKAN pengujian untuk menemukan bug sebanyak mungkin, dan keduanya menggunakan tes untuk memverifikasi bahwa program perangkat lunak berfungsi dengan baik dan dapat ditemukan pada saat itu. Tidak seorang pun dengan setengah petunjuk menjual perangkat lunak tanpa pengujian dan tidak seorang pun dengan setengah petunjuk berharap bahwa pengujian akan membuat kode yang mereka jual sepenuhnya bebas bug.

Jadi, perbedaan antara TDD dan bukan-TDD bukanlah bahwa tes sedang dilakukan. Perbedaannya adalah ketika tes ditulis. Dalam tes TDD ditulis SEBELUM perangkat lunak. Bukan tes TDD ditulis setelah atau bersamaan dengan perangkat lunak.

Masalah yang saya lihat berkenaan dengan yang terakhir adalah bahwa pengujian kemudian cenderung menargetkan perangkat lunak yang ditulis lebih dari hasil atau spesifikasi yang diinginkan. Bahkan dengan tim pengujian terpisah dari tim pengembangan, tim pengujian cenderung melihat perangkat lunak, bermain dengannya, dan menulis tes yang menargetkannya.

Satu hal yang telah diperhatikan berkali-kali oleh mereka yang mempelajari kesuksesan proyek, adalah seberapa sering seorang pelanggan akan menata apa yang mereka inginkan, orang-orang pengembangan lari dan menulis sesuatu, dan ketika mereka kembali ke pelanggan mengatakan "selesai" ternyata benar-benar BUKAN apa yang diminta pelanggan. "Tapi itu melewati semua tes ..."

Tujuan TDD adalah untuk memecahkan "argumen melingkar" ini dan memberikan dasar untuk pengujian yang menguji perangkat lunak yang bukan perangkat lunak itu sendiri. Tes ditulis untuk menargetkan perilaku yang diinginkan oleh "pelanggan". Perangkat lunak ini kemudian ditulis untuk lulus tes tersebut.

TDD adalah bagian dari solusi yang dimaksudkan untuk mengatasi masalah ini. Itu bukan satu-satunya langkah yang Anda buat. Hal lain yang perlu Anda lakukan adalah memastikan ada lebih banyak umpan balik pelanggan dan lebih sering.

Dalam pengalaman saya, TDD adalah hal yang sangat sulit untuk berhasil diimplementasikan. Sulit untuk mendapatkan tes tertulis sebelum ada produk karena banyak pengujian otomatis memerlukan sesuatu untuk dimainkan agar perangkat lunak otomasi berfungsi dengan benar. Juga sulit untuk mendapatkan pengembang yang tidak terbiasa dengan pengujian unit untuk melakukannya. Berkali-kali saya memberi tahu orang-orang di tim saya untuk menulis tes PERTAMA. Saya tidak pernah benar-benar mendapatkannya untuk melakukannya. Pada akhirnya, kendala waktu dan politik menghancurkan semua upaya sehingga kita bahkan tidak melakukan tes unit sama sekali. Hal ini tentu saja mengarah, tak terhindarkan, pada desain yang secara tidak sengaja dan sangat digabungkan sehingga bahkan jika kita ingin, sekarang akan menjadi sangat mahal untuk diimplementasikan. Menghindari ITULAH apa yang akhirnya disediakan TDD untuk pengembang.

Edward Strange
sumber
+1 Terima kasih atas jawaban komprehensifnya, Nuh. Saya setuju bahwa perbedaan utama antara TDD dan bukan-TDD adalah ketika tes ditulis. Namun, saya juga berpikir bahwa "D" pertama di TDD adalah singkatan dari "driven", yang berarti bahwa, dalam TDD, seluruh pengembangan didorong oleh pengujian. Itulah yang menurut saya paling membingungkan. Saya tidak punya masalah dengan tes menulis sebelum benar-benar membangun apa yang akan diuji. Tapi membiarkan tes berjalan? Bagaimana hal itu berbeda dari lampu hijau untuk melakukan apa pun selama yang dangkal (yaitu hasil) baik-baik saja?
CesarGon
Nah, Cesar, apa yang akan Anda usulkan sebagai kriteria objektif yang lebih baik untuk memutuskan kapan tugas pengembangan selesai? Jika, seperti dalam TDD, tes adalah spesifikasi yang ditargetkan pengembang, maka pengembang telah melakukan tugasnya saat tes berlalu, bukan? Ya, bisa ada kekurangan dalam pengujian seperti halnya ada kekurangan dalam spesifikasi apa pun. Itu bukan tugas pengembang untuk diselesaikan. Jika tes cacat maka diperbaiki, maka pengembangan menargetkan target baru, dan ketika semuanya hijau mereka selesai. Ini bekerja karena selalu ada tes untuk lulus ... tidak ada, tambahan bulu tidak berdokumen.
Edward Strange
3
Mungkin saya tidak mengekspresikan diri saya dengan jelas. Tes mungkin merupakan cara yang baik untuk menentukan kapan Anda selesai. Tetapi saya tidak berpikir mereka adalah cara yang baik untuk memutuskan apa yang harus Anda bangun. Dan, dalam TDD, saya menemukan bahwa orang menggunakan tes untuk memutuskan apa yang seharusnya mereka buat. Apakah itu pengalaman Anda juga?
CesarGon
Tidak. Bangunan kami otomatis. Mereka dipicu oleh perubahan. Seperti saya katakan, TDD hanya bagian dari solusi.
Edward Strange
9

Desain dulu

TDD bukan alasan untuk melewati desain. Saya telah melihat banyak lompatan dalam kereta musik "lincah" karena mereka meskipun mereka dapat mulai coding segera. Agile yang tangkas akan membuat Anda membuat kode stat lebih cepat daripada praktik bagus rekayasa (bidang lain) yang mengilhami proses air terjun.

Tapi tes awal

Ketika seseorang mengatakan bahwa tes tersebut mendorong desain, itu berarti bahwa seseorang dapat menggunakan tes sangat awal dalam fase desain, jauh sebelum itu selesai. Melakukan tes ini akan sangat mempengaruhi desain Anda dengan menantang area abu-abu dan mengadu domba itu dengan dunia nyata jauh sebelum produk selesai. memaksa Anda untuk sering kembali ke desain dan menyesuaikannya untuk memperhitungkan ini.

Uji dan desain ... satu dan sama

Menurut pendapat saya TDD hanya membawa tes menjadi bagian integral dari desain, bukan sesuatu yang dilakukan pada akhirnya untuk memvalidasinya. Ketika Anda mulai menggunakan TDD semakin banyak Anda mendapatkan dalam pola pikir tentang bagaimana menghancurkan / merusak sistem Anda saat Anda mendesainnya. Secara pribadi saya tidak selalu melakukan tes saya terlebih dahulu. Tentu saya melakukan tes (unit) yang jelas pada antarmuka tetapi hasil nyata berasal dari tes integrasi dan spesifikasi yang saya buat ketika saya memikirkan cara baru dan kreatif yang dapat dipecah oleh desain ini. Segera setelah saya memikirkan suatu cara, saya membuat kode tes untuk itu dan melihat apa yang terjadi. Terkadang saya dapat hidup dengan konsekuensinya, dalam hal ini saya memindahkan tes dalam proyek terpisah yang bukan bagian dari bangunan utama (karena akan terus gagal).

Lalu siapa yang mengemudikan pertunjukan?

Dalam TDD, penggerak di sini berarti bahwa pengujian Anda memengaruhi desain Anda dengan sangat kuat sehingga orang dapat merasakan bahwa mereka benar-benar mengendarainya. Namun itu berhenti di situ, dan di sini saya mengerti keprihatinan Anda, itu agak menakutkan ... siapa yang mendorong pertunjukan?

ANDA mengemudi, bukan tes. Tes ada di sana sehingga saat Anda bergerak maju Anda mendapatkan tingkat kepercayaan yang baik pada apa yang telah Anda buat sehingga memungkinkan Anda untuk membangun lebih lanjut dengan mengetahui bahwa itu didasarkan pada dasar yang kuat.

padat selama tes solid

Persis , karenanya digerakkan dalam TDD. Bukan begitu banyak tes yang menggerakkan semuanya, tetapi mereka akan memiliki pengaruh yang sangat mendalam atas bagaimana Anda melakukan sesuatu, pada bagaimana Anda merancang dan berpikir sistem Anda sehingga Anda akan mendelegasikan sebagian besar proses pemikiran Anda untuk menguji dan sebagai imbalannya mereka akan memiliki pengaruh yang mendalam pada desain Anda.

ya tetapi jika saya melakukan itu dengan jembatan saya ....

berhenti di sana ... rekayasa perangkat lunak SANGAT berbeda dari praktik rekayasa lainnya di luar sana. Sebenarnya rekayasa perangkat lunak sebenarnya memiliki lebih banyak kesamaan dengan literatur. Seseorang dapat mengambil buku yang sudah jadi, merobek 4 bab dari itu dan menulis dua bab baru untuk menggantikannya memasukkannya kembali ke dalam buku dan Anda masih memiliki buku yang bagus. Dengan pengujian dan perangkat lunak yang baik Anda dapat merobek bagian mana pun dari sistem Anda dan menggantinya dengan yang lain dan biaya untuk melakukannya tidak jauh lebih tinggi daripada yang diciptakannya. Bahkan, jika Anda melakukan tes dan membiarkannya mempengaruhi desain Anda, mungkin lebih murah daripada membuatnya di tempat pertama karena Anda akan memiliki tingkat kepercayaan tertentu bahwa penggantian ini tidak akan merusak apa yang dicakup oleh tes.

Jika begitu bagus, kenapa tidak selalu berhasil?

Karena pengujian membutuhkan pola pikir yang SANGAT berbeda dari membangun. Tidak setiap orang dapat kembali dan dari sana, pada kenyataannya beberapa orang tidak akan dapat membangun tes yang tepat hanya karena mereka tidak dapat menetapkan pikiran mereka untuk menghancurkan ciptaan mereka. Ini akan menghasilkan proyek dengan terlalu sedikit pengujian atau pengujian yang cukup untuk mencapai metrik target (cakupan kode muncul di benak). Mereka akan senang tes jalur dan tes pengecualian tetapi akan melupakan kasus sudut dan kondisi batas.

Yang lain hanya akan bergantung pada tes untuk desain sebagian atau seluruhnya. Setiap anggota melakukan hal itu kemudian berintegrasi dengan satu sama lain. Desain adalah alat komunikasi yang pertama dan terutama, taruhannya kami atur di tanah untuk mengatakan di sinilah saya akan berada, sketsa yang mengatakan di sinilah pintu dan jendela berada. Tanpa ini, perangkat lunak Anda akan hancur terlepas dari berapa banyak tes yang Anda lakukan. Integrasi dan penggabungan akan selalu menyakitkan dan mereka akan kekurangan tes pada level abstraksi tertinggi.

Untuk tim-tim ini, TDD mungkin bukan jalan yang harus ditempuh.

Newtopian
sumber
7

Dengan TDD Anda cenderung tidak menulis kode yang tidak mudah atau cepat untuk diuji. Ini mungkin tampak seperti hal kecil, tetapi dapat memiliki dampak mendalam pada proyek karena berdampak pada betapa mudahnya untuk melakukan refactor, menguji, mereproduksi bug dengan pengujian dan memverifikasi perbaikan.

Juga lebih mudah bagi pengembang baru di proyek untuk mempercepat ketika Anda memiliki kode faktor yang lebih baik didukung oleh tes.

Alb
sumber
2
Saya suka ini - ini menekankan titik bahwa tidak begitu banyak TDD yang menciptakan manfaat (meskipun memiliki unit test jelas memiliki nilai yang sangat besar) sebagai jenis kode yang dihasilkannya dalam arti yang dapat diuji (dalam isolasi) dan semua jenis hal baik mengikuti dari itu (pemisahan kekhawatiran, IoC dan injeksi ketergantungan, dll, dll)
Murph
1
@Murph yeah TDD membantu membuat Anda jujur ​​:)
Alb
1
Sejujurnya saya tidak benar-benar yakin dengan argumen "lebih mudah untuk mempercepat" - tes mungkin membantu, tetapi kode (secara keseluruhan, tidak harus dalam isolasi) dapat agak sulit untuk memecahkan kode karena beberapa hal akan muncul seolah-olah dengan sihir misalnya Anda tidak tahu implementasi IInjectedThing apa yang Anda gunakan.
Murph
@Murph Teorinya adalah bahwa implementasi IInjectedThing juga dirancang dengan baik dan dicakup oleh tes yang baik, sehingga Anda tidak benar-benar perlu tahu apa itu untuk dapat memahami kelas yang disuntikkan ke dalamnya.
Adam Lear
@ Anna - ya, sampai batas tertentu ... jika Anda mencoba mencari tahu di mana ada sesuatu yang rusak (Saya selalu merasa perburuan bug adalah cara yang baik untuk menemukan pijakan di proyek) atau di mana ada sesuatu yang perlu diubah / menambahkan Anda harus tahu di mana. Bahkan jika itu di mana dikemas dengan baik Anda masih perlu menemukannya ... dan jika itu berarti mengganti sesuatu (implementasi baru IWhatsit) maka Anda perlu tahu bagaimana menggunakan implementasi alternatif. Sekali lagi, saya tidak menyangkal bahwa konstruksinya buruk - terlalu banyak bukti yang bertentangan - melainkan menyarankan bahwa beberapa hal mungkin kurang jelas.
Murph
5

Saya sudah banyak memikirkan hal ini, walaupun saya sendiri tidak terlalu banyak berlatih TDD. Tampaknya ada korelasi (kuat?) Positif antara kualitas kode dan mengikuti TDD.

1) Yang pertama saya ambil adalah, ini (terutama) bukan karena TDD menambahkan "kualitas yang lebih baik" ke dalam kode (seperti itu), itu lebih seperti TDD membantu menyingkirkan bagian dan kebiasaan terburuk, dan secara tidak langsung meningkatkan kualitas.

Saya bahkan akan menganjurkan bahwa itu bukan tes itu sendiri - itu adalah proses penulisan tes itu. Sulit untuk menulis tes untuk kode yang buruk, dan sebaliknya. Dan menjaga ini di belakang kepala saat pemrograman, menghilangkan banyak kode buruk.

2) Sudut pandang lain (ini semakin filosofis) adalah mengikuti kebiasaan mental tuannya. Anda tidak belajar menjadi seorang master dengan mengikuti "kebiasaan luar" -nya (seperti, janggut panjang itu baik), Anda harus mempelajari cara berpikir internalnya, dan ini sulit. Dan entah bagaimana membuat (pemula) programmer mengikuti TDD, menyelaraskan cara berpikir mereka lebih dekat dengan orang-orang dari master.

Maglob
sumber
+1 Saya pikir Anda berhasil, Maglob. Saya terutama menyukai penjelasan Anda bahwa "TDD membantu menyingkirkan bagian dan kebiasaan terburuk, [...] secara tidak langsung meningkatkan kualitas". Dan analogi janggut panjangnya juga sangat bagus.
CesarGon
Anda tidak menulis tes untuk kode yang buruk, tetapi Anda menulis tes dan kemudian menulis kode untuk lulus ujian.
Maglob, untuk cinta dari sisi yang lebih praktis, Anda mendapatkan yang terbaik. @ Thorbjørn, saya pikir Maglob lebih tertinggal di sepanjang garis bahwa jika desain yang diproyeksikan menyebalkan, tes Anda pasti harus langsung menyedot hingga tingkat kesialan yang Anda coba terwujud dan bau busuk itu harus berbau dalam tes Anda sebelum Anda bahkan mulai menulis kode yang sebenarnya.
Filip Dupanović
3

Pendekatan "tulis tes + refactor sampai lulus" terlihat sangat anti-rekayasa.

Anda tampaknya memiliki kesalahpahaman tentang refactoring dan TDD.

Refactoring kode adalah proses mengubah kode sumber program komputer tanpa mengubah perilaku fungsional eksternalnya untuk meningkatkan beberapa atribut nonfungsional dari perangkat lunak.

Dengan demikian Anda tidak dapat refactor kode sampai lolos.

Dan TDD, khususnya pengujian unit (yang saya anggap sebagai peningkatan inti, karena tes lain tampaknya agak masuk akal bagi saya), bukan tentang mendesain ulang komponen hingga berfungsi. Ini adalah tentang mendesain komponen dan bekerja pada implementasi hingga komponen berfungsi seperti yang dirancang.

Penting juga untuk benar-benar memahami, bahwa pengujian unit adalah tentang pengujian unit . Karena kecenderungan untuk selalu menulis banyak hal dari awal, penting untuk menguji unit tersebut. Seorang insinyur sipil sudah mengetahui spesifikasi unit yang ia gunakan (bahan yang berbeda) dan dapat mengharapkan mereka bekerja. Ini adalah dua hal yang sering tidak berlaku untuk insinyur perangkat lunak, dan sangat pro-teknik untuk menguji unit sebelum menggunakannya, karena itu berarti menggunakan komponen yang teruji dan berkualitas tinggi.
Jika seorang insinyur sipil mempunyai ide untuk menggunakan beberapa jaringan serat baru untuk membuat atap untuk menutupi stadion, Anda akan mengharapkan dia untuk mengujinya sebagai satu unit, yaitu mendefinisikan spesifikasi yang diperlukan (misalnya berat, permeabilitas, stabilitas, dll.) Dan setelah itu uji dan sempurnakan sampai bertemu mereka.

Itu sebabnya TDD berfungsi. Karena jika Anda membuat perangkat lunak dari unit yang diuji, kemungkinannya jauh lebih baik, ketika Anda menyambungkannya dan jika tidak, Anda dapat mengharapkan masalah ada dalam kode lem Anda, dengan asumsi tes Anda memiliki cakupan yang baik.

sunting:
Refactoring berarti: tidak ada perubahan fungsi. Satu poin dari pengujian unit penulisan adalah untuk memastikan, bahwa refactoring tidak melanggar kode. Jadi TDD dimaksudkan untuk memastikan, bahwa refactoring tidak memiliki efek samping.
Granularitas bukan subjek perspektif, karena seperti yang saya katakan, unit menguji unit tes dan bukan sistem, di mana granularitas didefinisikan secara tepat.

TDD mendorong arsitektur yang baik. Ini mengharuskan Anda untuk mendefinisikan dan mengimplementasikan spesifikasi untuk semua unit Anda, memaksa Anda untuk mendesainnya sebelum implementasi, yang sangat bertentangan dengan apa yang tampaknya Anda pikirkan. TDD menentukan pembuatan unit, yang dapat diuji secara individual dan dengan demikian sepenuhnya dipisahkan.
TDD tidak berarti saya melakukan tes perangkat lunak pada kode spaghetti dan mengaduk pasta sampai habis.

Dalam kontradiksi dengan teknik sipil, dalam rekayasa perangkat lunak suatu proyek biasanya terus berkembang. Dalam teknik sipil, Anda memiliki persyaratan untuk membangun jembatan di posisi A, yang dapat mengangkut x ton dan cukup lebar untuk n kendaraan per jam.
Dalam rekayasa perangkat lunak, pelanggan pada dasarnya dapat memutuskan pada titik mana pun (mungkin setelah selesai), ia menginginkan jembatan berlipat ganda, dan bahwa ia ingin jembatan itu terhubung dengan jalan raya terdekat, dan bahwa ia ingin jembatan itu menjadi jembatan pengangkat, karena perusahaannya Baru-baru ini mulai menggunakan kapal layar.
Insinyur perangkat lunak ditugaskan untuk mengubah desain. Bukan karena desain mereka cacat, tetapi karena itu adalah modus operandi. Jika perangkat lunak direkayasa dengan baik, maka dapat dirancang ulang pada level tinggi, tanpa harus menulis ulang semua komponen level rendah.

TDD adalah tentang membangun perangkat lunak dengan komponen yang diuji secara terpisah dan sangat terpisah. Dikelola dengan baik, ini akan membantu Anda untuk menanggapi perubahan persyaratan secara signifikan lebih cepat dan lebih aman, daripada tanpa.

TDD menambahkan persyaratan untuk proses pengembangan, tetapi tidak melarang metode jaminan kualitas lainnya. Memang, TDD tidak menyediakan keamanan yang sama dengan verifikasi formal, tetapi sekali lagi, verifikasi formal sangat mahal dan tidak mungkin digunakan pada tingkat sistem. Dan tetap, jika Anda mau, Anda bisa menggabungkan keduanya.

TDD juga mencakup pengujian selain pengujian unit, yang dilakukan pada tingkat sistem. Saya menemukan ini mudah untuk dijelaskan tetapi sulit untuk dieksekusi dan sulit untuk diukur. Juga, mereka cukup masuk akal. Sementara saya benar-benar melihat kebutuhan mereka, saya tidak benar-benar menghargai mereka sebagai ide.

Pada akhirnya, tidak ada alat yang benar-benar memecahkan masalah. Alat hanya mempermudah penyelesaian masalah. Anda dapat bertanya: Bagaimana pahat akan membantu saya dengan arsitektur yang hebat? Nah jika Anda berencana untuk melakukan dinding lurus, batu bata lurus sangat membantu. Dan ya, memang, jika Anda memberikan alat itu kepada orang idiot, ia mungkin akan membantingnya melalui kakinya pada akhirnya, tapi itu bukan kesalahan pahat, sebanyak itu bukan cacat TDD yang memberikan keamanan palsu kepada pemula, yang tidak menulis tes yang bagus.
Jadi pada intinya, dapat dikatakan TDD bekerja jauh lebih baik daripada tanpa TDD.

back2dos
sumber
Saya tidak berpikir saya memiliki kesalahpahaman; Saya setuju dengan definisi kode refactoring yang telah Anda posting, tetapi saya juga berpikir bahwa Anda perlu melihat rincian perubahan kode. Ketika Anda mengatakan "proses mengubah kode sumber program komputer", Anda perlu menyadari bahwa, dari perspektif keseluruhan tertentu, perilaku tidak berubah, tetapi perilaku bagian memang berubah. Begitulah cara perubahan dilakukan. Selain itu, saya mendengar Anda tentang mengapa TDD berfungsi (dan saya bagikan), tetapi bagaimana arsitektur ditangani sesuai dengan posting asli saya?
CesarGon
@CesarGon: Posting diperbarui.
back2dos
2

Saya tidak suka Anda mengatakan 'tes, daripada pengguna, menetapkan persyaratan'. Saya pikir Anda hanya mempertimbangkan pengujian unit di TDD, sedangkan itu juga mencakup pengujian integrasi.

Selain menguji perpustakaan yang menjadi dasar perangkat lunak, tulis tes yang mencakup interaksi pengguna Anda dengan perangkat lunak / situs web / apa pun. Ini datang langsung dari pengguna, dan perpustakaan seperti mentimun (http://cukes.info) bahkan dapat membiarkan pengguna Anda menulis tes sendiri, dalam bahasa alami.

TDD juga mendorong fleksibilitas dalam kode - jika Anda menghabiskan selamanya merancang arsitektur sesuatu, itu akan sangat sulit untuk membuat perubahan itu nanti jika perlu. Mulailah dengan menulis beberapa tes, kemudian tulis kode kecil yang lulus tes tersebut. Tambahkan lebih banyak tes, tambahkan lebih banyak kode. Jika Anda perlu mengubah kode secara radikal, pengujian Anda masih berlaku.

Dan tidak seperti jembatan dan mobil, satu perangkat lunak dapat mengalami perubahan besar selama masa pakainya, dan melakukan refactoring yang rumit tanpa tes tertulis terlebih dahulu hanya meminta masalah.

sevenseacat
sumber
Saya mendengar Anda tentang manfaat yang Anda klaim untuk TDD. Tapi sejauh yang saya mengerti Anda tidak membahas masalah arsitektur dan kualitas tes yang secara eksplisit saya tanyakan dalam pertanyaan saya.
CesarGon
@CesarGon: Saya pikir pertanyaan spesifik Anda berlaku untuk semua jenis pengujian, bukan hanya TDD. Jadi saya hanya fokus pada fitur spesifik TDD yang 'berfungsi'.
sevenseacat
1
Tes integrasi jelas lebih masuk akal daripada tes unit yang berdiri sendiri. Sebagian besar kasus bug yang saya temukan tidak akan pernah ditemukan oleh unit test, hanya dengan menguji seluruh sistem nyata dengan semua baut dan peluitnya.
2

Saya pikir Anda mendekati titik pertama dari sudut yang salah.

Dari sudut pandang teoretis, kami membuktikan bahwa sesuatu bekerja dengan memeriksa titik kegagalan. Itulah metode yang digunakan. Mungkin ada banyak cara lain Anda dapat membuktikan bahwa sesuatu itu fungsional, tetapi TDD telah memantapkan dirinya karena kesederhanaan dari pendekatan bit-bijaksana: jika tidak rusak itu berfungsi.

Dalam praktiknya, ini hanya secara terbuka diterjemahkan menjadi: kita sekarang dapat beralih ke hal berikutnya (setelah kita berhasil menerapkan TDD untuk memenuhi semua predikat). Jika Anda mendekati TDD dari perspektif ini, maka ini bukan tentang "tulis tes + refactor sampai lulus" ini lebih tentang menyelesaikan ini, saya sekarang sepenuhnya berfokus pada fitur berikutnya sebagai hal yang paling penting sekarang .

Pikirkan bagaimana ini berlaku untuk teknik sipil. Kami sedang membangun stadion yang dapat menampung audiens publik 150000 orang. Setelah kami membuktikan bahwa integritas struktural stadion sehat, kami sudah puas dengan keselamatan . Kita sekarang dapat fokus pada masalah-masalah lain yang segera menjadi penting, seperti kamar kecil, kedai makanan, tempat duduk, dll ... menjadikan pengalaman penonton yang lebih menyenangkan. Ini adalah penyederhanaan yang berlebihan, karena ada lebih banyak hal untuk TDD, tetapi intinya adalah bahwa Anda tidak membuat yang terbaik pengalaman pengguna mungkin jika Anda berfokus pada fitur-fitur baru dan menarik dan menjaga integritas pada saat yang sama. Anda mendapatkannya setengah jalan dalam kedua kasus. Maksud saya, bagaimana Anda bisa tahu persis caranyabanyak toilet dan di mana Anda harus tempat untuk 150000 orang? Saya jarang melihat stadion runtuh dalam hidup saya sendiri, tetapi saya harus mengantri selama setengah waktu dalam banyak kesempatan. Yang mengatakan bahwa masalah toilet bisa dibilang lebih kompleks dan jika para insinyur dapat menghabiskan lebih sedikit waktu untuk keselamatan, mereka akhirnya mungkin dapat menyelesaikan masalah toilet.

Poin kedua Anda tidak relevan, karena kami sudah sepakat bahwa yang absolut adalah upaya bodoh dan karena Hank Moody mengatakan mereka tidak ada (tetapi saya tidak dapat menemukan referensi untuk itu).

Filip Dupanović
sumber
+1 untuk penjelasan yang bagus tentang poin pertama saya, dan untuk referensi ke Hank Moody. Mulia.
CesarGon
2
Terima kasih, saya menghargainya. Saya memandang TDD lebih sebagai fenomena psikologis, daripada pendekatan / proses teknis. Tapi itu hanya pandangan dunia saya tentang masalah ini.
Filip Dupanović
Bisakah Anda tahu persis berapa banyak toilet dan di mana mereka harus ditempatkan? Jawabannya adalah ya - tanyakan arsitek mana saja dan mereka akan memberi tahu Anda bahwa informasi ini dibuat di muka dan kadang-kadang dengan data statistik yang jelas untuk mendukungnya.
gbjbaanb
1

TDD dalam rekayasa perangkat lunak adalah praktik yang baik, dengan cara yang sama seperti penanganan kesalahan dalam aplikasi adalah praktik yang baik serta pencatatan dan diagnostik (meskipun itu merupakan bagian dari penanganan kesalahan).

TDD tidak dapat digunakan sebagai alat untuk mengurangi pengembangan perangkat lunak menjadi pengkodean coba-coba. Tapi tetap saja, sebagian besar programmer menatap log runtime, menonton pengecualian dalam debugger atau menggunakan tanda-tanda kegagalan / kesuksesan lainnya selama fase pengembangan mereka yang terdiri dari pengkodean / kompilasi / menjalankan aplikasi - sepanjang hari.

TDD hanyalah cara untuk memformalkan dan mengotomatiskan langkah-langkah tersebut untuk menjadikan Anda sebagai pengembang lebih produktif.

1) Anda tidak dapat membandingkan rekayasa perangkat lunak dengan konstruksi jembatan, fleksibilitas dalam konstruksi jembatan tidak jauh dari merancang program perangkat lunak. Membangun jembatan seperti menulis program yang sama berulang-ulang menjadi mesin yang macet. Jembatan tidak dapat diduplikasi dan digunakan kembali sebagaimana perangkat lunak dapat. Setiap jembatan unik dan harus dibuat. Hal yang sama berlaku untuk mobil dan desain lainnya.

Hal tersulit dalam rekayasa perangkat lunak adalah mereproduksi kesalahan, ketika jembatan gagal biasanya sangat mudah untuk menentukan apa yang salah, dan secara teori mudah untuk mereproduksi kegagalan. Ketika sebuah program komputer gagal, itu bisa menjadi rangkaian peristiwa yang kompleks yang membawa sistem ke keadaan salah dan bisa sangat sulit untuk menentukan di mana kesalahannya. TDD dan uji unit membuatnya lebih mudah untuk menguji ketahanan komponen perangkat lunak, perpustakaan dan algoritma.

2) Menggunakan unit test lemah dan test case dangkal yang tidak menekankan sistem untuk membangun rasa percaya diri yang salah adalah praktik yang buruk. Mengabaikan kualitas arsitektur suatu sistem dan hanya memenuhi tes tentu saja sama buruknya. Tapi menipu di tempat konstruksi untuk gedung pencakar langit atau jembatan untuk menghemat bahan dan tidak mengikuti cetak biru sama buruknya dan itu terjadi setiap saat ...

Ernelli
sumber
Saya tidak setuju dengan implikasi Anda bahwa mudah dalam sistem fisik (yaitu non-perangkat lunak) untuk mereproduksi kegagalan. Lihatlah, kerja keras yang sangat rumit yang diperlukan untuk menentukan akar penyebab kegagalan mekanik dalam kecelakaan lalu lintas udara, misalnya.
CesarGon
Hm, sekarang Anda membandingkan Airliner yang menabrak dengan jembatan yang gagal, jembatan yang biasanya tidak bisa terbang, kasing tertutup. Tetapi perbandingan antara pesawat terbang dan perangkat lunak terkadang valid. Kedua area tersebut sangat kompleks dan membutuhkan metodologi tes terstruktur. Jadi ketika sebuah jembatan gagal, Anda tahu bahwa itu sudah kelebihan beban. Ketika sebuah pesawat jatuh, Anda tahu bahwa kondisi abnormal terbang di atas tanah gagal, tetapi alasannya biasanya memerlukan penyelidikan menyeluruh sama dengan kegagalan perangkat lunak.
Ernelli
Jembatan dapat diduplikasi - atau paling tidak, cetak biru jembatan yang Anda beli dari arsitek dapat, secara kasar, dengan modifikasi yang sesuai dengan keadaan Anda. Intinya adalah bahwa jika Anda membutuhkan jembatan, Anda akan pergi ke arsitek dan dia akan memberi Anda daftar hanya beberapa jenis yang dapat Anda miliki - suspensi, kotak, lengkungan dll, dan daftar bahan yang terbatas untuk membangunnya.
gbjbaanb
1

Jika Anda menerima bahwa semakin cepat bug ditemukan, semakin sedikit biaya untuk memperbaikinya, maka itu saja membuat TDD berharga.

SnoopDougieDoug
sumber
1
Apakah Anda memiliki bukti bahwa bug ditemukan lebih cepat dalam pengaturan TDD? Juga, bagaimana dengan efek samping TDD, seperti dampak pada arsitektur?
CesarGon
0

TDD sebenarnya bukan tentang pengujian. Dan tentu saja itu bukan pengganti untuk pengujian yang baik. Apa yang diberikannya kepada Anda adalah desain yang dipikirkan dengan baik, mudah dikonsumsi konsumen, dan mudah dirawat serta diperbaiki lagi nantinya. Hal-hal itu pada gilirannya menyebabkan lebih sedikit bug dan desain perangkat lunak yang lebih baik dan lebih mudah beradaptasi. TDD juga membantu Anda memikirkan dan mendokumentasikan asumsi Anda, seringkali menemukan bahwa beberapa di antaranya salah. Anda menemukan ini sangat awal dalam proses.

Dan sebagai manfaat sampingan yang bagus, Anda memiliki serangkaian besar tes yang dapat Anda jalankan untuk memastikan bahwa refactoring tidak mengubah perilaku (input dan output) perangkat lunak Anda.

Marcie
sumber
6
-1. Banyak orang terus mengatakan ini, tetapi saya belum melihat keajaiban yang membuatnya terjadi.
Bart van Ingen Schenau
@ Bart van Ingen Schenau, sudahkah Anda melakukan TDD? Saya telah melakukannya selama sekitar 4 tahun, dan saya pasti melihat "keajaiban" terjadi.
Marcie
0

Saya akan memberi Anda jawaban singkat. Biasanya TDD dipandang dengan cara yang salah sama seperti pengujian unit. Saya tidak pernah mengerti pengujian unit sampai baru-baru ini setelah menonton video pembicaraan teknologi yang bagus. Pada dasarnya TDD hanya menyatakan Anda ingin hal-hal berikut BEKERJA. Mereka HARUS dilaksanakan. Kemudian Anda mendesain sisa perangkat lunak seperti biasa.

Jenisnya seperti menulis menggunakan case untuk perpustakaan sebelum mendesain perpustakaan. Kecuali Anda dapat mengubah use case di perpustakaan dan Anda mungkin tidak untuk TDD (saya menggunakan TDD untuk desain API). Anda juga didorong untuk menambahkan lebih banyak tes dan memikirkan input / penggunaan liar yang mungkin didapat dari tes ini. Saya merasa berguna saat menulis perpustakaan atau API di mana jika Anda mengubah sesuatu, Anda harus tahu Anda memecahkan sesuatu. Dalam sebagian besar perangkat lunak sehari-hari, saya tidak repot-repot karena mengapa saya perlu uji kasus untuk pengguna menekan tombol atau jika saya ingin menerima daftar CSV atau daftar dengan satu entri per baris ... Itu tidak masalah saya mengizinkan untuk mengubahnya jadi saya seharusnya / tidak bisa menggunakan TDD.


sumber
0

Perangkat lunak adalah organik, ketika rekayasa struktural beton.

Ketika Anda membangun jembatan Anda, itu akan tetap menjadi jembatan dan kecil kemungkinannya akan berkembang menjadi sesuatu yang lain dalam waktu singkat. Perbaikan akan dilakukan selama berbulan-bulan dan bertahun-tahun, tetapi tidak berjam-jam seperti dalam perangkat lunak.

Saat Anda menguji secara terpisah, biasanya ada dua jenis kerangka kerja yang dapat Anda gunakan. Kerangka kerja terbatas dan tidak dibatasi. Kerangka kerja yang tidak dibatasi (dalam .NET) memungkinkan Anda untuk menguji dan mengganti semuanya, terlepas dari pengubah akses. Yaitu Anda dapat mematikan dan mengejek komponen pribadi dan yang dilindungi.

Sebagian besar proyek yang saya lihat menggunakan kerangka kerja terbatas (RhinoMocks, NSubstitute, Moq). Ketika Anda menguji dengan kerangka kerja ini, Anda harus merancang aplikasi Anda sedemikian rupa sehingga Anda bisa menyuntikkan dan mengganti dependensi saat runtime. Ini menyiratkan bahwa Anda harus memiliki desain yang digabungkan secara longgar. Desain yang digabungkan secara longgar (bila dilakukan dengan benar) menyiratkan pemisahan kekhawatiran yang lebih baik, yang merupakan hal yang baik.

Sebagai rangkuman, saya percaya bahwa berpikir di balik ini, adalah bahwa jika desain Anda dapat diuji, maka itu digabungkan secara longgar dan memiliki pemisahan keprihatinan yang baik.

Di samping catatan, saya telah melihat aplikasi yang benar-benar dapat diuji, tetapi ditulis dengan buruk dari perspektif desain berorientasi objek.

CodeART
sumber
0

Mengapa TDD berfungsi?

Tidak.

Klarifikasi: tes otomatis lebih baik daripada tidak ada tes. Namun saya pribadi berpikir bahwa sebagian besar unit test adalah pemborosan karena biasanya tautologis (yaitu mengatakan hal-hal yang jelas dari kode aktual yang diuji) dan tidak dapat dengan mudah dibuktikan bahwa mereka konsisten, tidak berlebihan dan mencakup semua kasus perbatasan (di mana kesalahan biasanya terjadi ).

Dan yang paling penting: Desain perangkat lunak yang baik tidak secara ajaib keluar dari tes seperti yang diiklankan oleh banyak penginjil tangkas / TDD. Semua orang yang mengklaim sebaliknya tolong berikan tautan ke penelitian ilmiah yang ditinjau oleh rekan sejawat yang membuktikan hal ini, atau setidaknya rujukan ke beberapa proyek sumber terbuka di mana manfaat TDD dapat dipelajari secara potensial dengan sejarah perubahan kodenya.

Kola
sumber