Mengapa tidak menulis semua tes sekaligus saat melakukan TDD?

55

Siklus Merah - Hijau - Reaktor untuk TDD telah ditetapkan dan diterima dengan baik. Kami menulis satu tes unit gagal dan membuatnya lulus sesederhana mungkin. Apa manfaat dari pendekatan ini daripada menulis banyak tes unit gagal untuk kelas dan membuat semuanya lulus sekaligus.

Test suite masih melindungi Anda dari penulisan kode yang salah atau membuat kesalahan di tahap refactoring, jadi apa salahnya? Terkadang lebih mudah untuk menulis semua tes untuk kelas (atau modul) terlebih dahulu sebagai bentuk 'brain dump' untuk dengan cepat menuliskan semua perilaku yang diharapkan dalam sekali jalan.

RichK
sumber
20
Lakukan yang terbaik untuk Anda (setelah beberapa percobaan). Mengikuti dogma secara membabi buta tidak pernah merupakan hal yang baik.
Michael Borgwardt
6
Saya berani mengatakan bahwa menulis semua tes Anda sekaligus seperti menulis semua kode aplikasi Anda sekaligus.
Michael Haren
1
@MichaelHaren Semua tes untuk kelas (atau modul fungsional), maaf atas kebingungan
RichK
3
Mengatasi masalah "brain dump": Kadang-kadang ada poin dalam pengujian / pengkodean ketika Anda menyadari perlunya beberapa tes input spesifik yang berbeda, dan ada kecenderungan ingin memanfaatkan kejernihan dari realisasi itu sebelum Anda terganggu dengan hal kecil dari pengkodean. Saya biasanya mengelolanya dengan mempertahankan daftar terpisah (misalnya Mylyn), atau dengan daftar komentar di kelas Uji hal-hal yang berbeda yang ingin saya ingat untuk diuji (misalnya // test null case). Namun, saya masih hanya kode satu tes pada suatu waktu, dan sebagai gantinya saya turun daftar secara sistematis.
Sam Goldberg
1
baik saya tidak tahu mengapa tidak ada yang menyebutkan ini, tetapi Anda TIDAK BISA menulis semua tes sekaligus. Menulis semua tes sebelumnya sama persis dengan melakukan BDUF . Dan apa yang telah diajarkan sejarah tentang BDUF? Ini hampir tidak pernah bekerja.
Songo

Jawaban:

50

Desain yang digerakkan oleh tes adalah tentang memperbaiki API Anda , bukan kode.

Manfaat dari menulis tes gagal paling sederhana pertama adalah bahwa Anda mendapatkan API Anda (yang pada dasarnya adalah Anda merancang dengan cepat) sesederhana mungkin. Di muka.

Segala kegunaan di masa depan (yang akan Anda tulis dalam tes selanjutnya) akan berasal dari desain sederhana awal, alih-alih desain yang optimal mengatasi kasus yang lebih kompleks.

pengguna1249
sumber
Poin luar biasa! Kami terkadang sangat tenggelam dalam menguji kode sehingga terkadang kami mengabaikan betapa pentingnya API dan model domain sebelum bahkan menulis tes pertama Anda.
maple_shaft
+1 untuk benar-benar mengatasi maksud Pengembangan yang Didorong Tes .
Joshua Drake
76

Ketika Anda menulis satu tes, Anda berkonsentrasi pada satu hal.
Dengan banyak tes, Anda menyebarkan perhatian pada banyak tugas, jadi itu bukan ide yang baik.

Abyx
sumber
8
Siapa yang akan menurunkan ini ?!
CaffGeek
6
@Chad aku bukan suara turun, tapi aku percaya jawaban ini melewatkan yang sudah jelas. Pengembangan Test Driven adalah tentang menggunakan Test (s) untuk mendorong desain kode. Anda menulis tes secara individual untuk mengembangkan desain, bukan hanya untuk testabilitas. Jika itu hanya tentang artefak tes maka ini akan menjadi jawaban yang baik, tetapi seperti itu kehilangan beberapa informasi penting.
Joshua Drake
7
Saya tidak mencatat ini tetapi; Sudah saya pikirkan. Jawabannya terlalu singkat untuk sebuah pertanyaan kompleks.
Mark Weston
2
+1 untuk berkonsentrasi pada satu hal pada satu waktu, kemampuan kita untuk melakukan banyak tugas berlebihan.
cctan
Ini jawaban paling sederhana yang mungkin bisa berhasil.
DNA
27

Salah satu kesulitan saat menulis tes unit adalah Anda menulis kode, dan itu sendiri berpotensi rawan kesalahan. Ada juga kemungkinan bahwa Anda mungkin akhirnya perlu mengubah tes Anda nanti sebagai hasil dari upaya refactoring saat Anda menulis kode implementasi Anda. Dengan TDD, ini berarti Anda berpotensi berakhir sedikit terbawa dengan pengujian Anda dan menemukan diri Anda perlu menulis ulang banyak kode tes yang pada dasarnya "belum diuji" saat implementasi Anda matang selama proyek berlangsung. Salah satu cara untuk menghindari masalah semacam ini adalah dengan hanya fokus melakukan satu hal pada satu waktu. Ini memastikan Anda meminimalkan dampak dari setiap perubahan pada tes Anda.

Tentu saja, ini sebagian besar akan tergantung pada bagaimana Anda menulis kode pengujian Anda. Apakah Anda menulis tes unit untuk setiap metode individu, atau Anda menulis tes yang berfokus pada fitur / persyaratan / perilaku? Pendekatan lain bisa menggunakan pendekatan Behavior Driven dengan kerangka kerja yang sesuai, dan fokus pada tes menulis seolah-olah mereka spesifikasi. Ini berarti mengadopsi metode BDD, atau mengadaptasi pengujian BDD jika Anda ingin tetap menggunakan TDD secara lebih formal. Atau Anda mungkin tetap berpegang sepenuhnya pada paradigma TDD, namun mengubah cara Anda menulis tes sehingga alih-alih berfokus sepenuhnya pada metode pengujian secara individual, Anda menguji perilaku secara lebih umum sebagai sarana untuk memenuhi spesifikasi fitur persyaratan yang Anda laksanakan secara spesifik.

Terlepas dari pendekatan spesifik yang Anda ambil, dalam semua kasus yang saya jelaskan di atas Anda menggunakan pendekatan uji-pertama, jadi meskipun mungkin tergoda untuk hanya mengunduh otak Anda ke dalam ruang uji yang indah, Anda juga ingin melawan godaan untuk melakukan lebih dari itu mutlak diperlukan. Setiap kali saya akan memulai test suite baru saya mulai mengulangi YAGNI untuk diri saya sendiri, dan kadang-kadang bahkan memasukkannya ke dalam komentar dalam kode saya untuk mengingatkan saya untuk tetap fokus pada apa yang segera penting, dan hanya melakukan minimum yang diperlukan untuk memuaskan persyaratan fitur yang akan saya implementasikan. Menempel pada Red-Green-Refactor membantu memastikan bahwa Anda akan melakukan ini.

S.Robins
sumber
Saya senang Anda menunjukkan perbedaan antara bagaimana seseorang menulis kode pengujian mereka. Beberapa suka menulis tes unit master tunggal yang mencakup setiap kemungkinan realistis input ke fungsi atau metode tunggal. Yang lain mengambil pendekatan BDD yang lebih banyak dengan unit test mereka. Perbedaan ini penting ketika menentukan apakah menulis seluruh rangkaian tes itu penting adalah praktik yang buruk atau tidak. Wawasan luar biasa!
maple_shaft
17

Saya pikir dengan melakukan ini, Anda kehilangan proses TDD. Dengan hanya menulis semua tes Anda di awal, Anda tidak benar-benar melalui proses pengembangan menggunakan TDD. Anda hanya menebak-nebak tes mana yang akan Anda butuhkan. Ini akan menjadi serangkaian tes yang sangat berbeda dari yang Anda akhirnya tulis jika Anda melakukannya satu per satu saat Anda mengembangkan kode Anda. (Kecuali jika program Anda bersifat sepele.)

ZweiBlumen
sumber
1
Sebagian besar aplikasi bisnis dan perusahaan bersifat sepele secara teknis dan melihat bagaimana sebagian besar aplikasi adalah bisnis dan perusahaan, sebagian besar aplikasi juga bersifat sepele.
maple_shaft
5
@maple_shaft - teknologi mungkin sepele, tetapi aturan bisnisnya tidak. Coba dan bangun aplikasi untuk 5 manajer, semua yang memiliki persyaratan berbeda dan menolak untuk mendengarkan BS tentang desain minimalis, sederhana, elegan, kurang-lebih-lebih-lebih.
JeffO
5
@ Jeffe 1) Ini bukan BS. 2) Desain minimalis yang elegan membutuhkan keterampilan pengembangan perangkat lunak yang baik. 3) Kemampuan untuk mengurangi persyaratan dari 5 manajer berbeda yang tidak memiliki lebih dari 5 menit seminggu untuk dihabiskan bersama Anda dan masih melakukan desain minimalis membutuhkan pengembang perangkat lunak yang sangat baik . Pro Tip: Pengembangan perangkat lunak lebih dari sekedar keterampilan coding, ini adalah negosiasi, percakapan dan kepemilikan. Anda harus menjadi anjing Alpha dan terkadang menggigitnya kembali.
maple_shaft
1
Jika saya mengerti dengan benar, jawaban ini memunculkan pertanyaan.
Konrad Rudolph
1
@maple_shaft Saya pikir itulah yang dilakukan Jeff O dengan komentarnya, bukan?
ZweiBlumen
10

Saya "menulis" semua tes yang dapat saya pikirkan di depan sambil "brainstorming", namun saya menulis setiap tes sebagai satu-satunya komentar yang menggambarkan tes tersebut.

Saya kemudian mengonversi satu tes ke kode dan melakukan pekerjaan sehingga akan mengkompilasi dan lulus . Seringkali saya memutuskan bahwa saya tidak memerlukan semua tes yang saya pikir saya lakukan, atau saya perlu tes yang berbeda, informasi ini hanya berasal dari penulisan kode untuk membuat tes lulus.

Masalahnya adalah Anda tidak dapat menulis tes dalam kode sampai Anda telah membuat metode dan kelas tes itu karena jika tidak, Anda hanya akan mendapatkan banyak kesalahan kompiler yang membuat Anda mengerjakan satu tes pada satu waktu.

Sekarang jika Anda menggunakan sistem seperti aliran spek ketika tes ditulis dalam "Bahasa Inggris" Anda mungkin ingin agar pelanggan menyetujui serangkaian tes saat Anda punya waktu, daripada hanya membuat tes tunggal.

Ian
sumber
1
Ya, sementara setuju dengan jawaban di atas yang menunjukkan masalah dengan pengkodean semua tes Anda terlebih dahulu, saya merasa sangat membantu untuk membuang pemahaman saya secara keseluruhan tentang bagaimana metode saat ini harus berperilaku sebagai serangkaian deskripsi pengujian tanpa kode apa pun. Proses penulisan ini cenderung memperjelas apakah saya benar-benar mengerti apa yang diperlukan dari kode yang akan saya tulis, dan apakah ada kasus tepi yang belum saya pikirkan. Saya merasa jauh lebih nyaman dengan pengkodean tes pertama dan kemudian lulus setelah saya menguraikan "gambaran umum" saya tentang bagaimana metode ini harus bekerja.
Mark Weston
10

Gagasan di balik TDD adalah iterasi cepat.

Jika Anda memiliki petak besar tes yang harus ditulis sebelum Anda harus menulis kode Anda, sulit untuk iteratif memperbaiki kode Anda.

Tanpa refactoring kode mudah Anda kehilangan banyak manfaat TDD.

linkerro
sumber
5

Dalam pengalaman (terbatas) saya dengan TDD, saya dapat memberi tahu Anda bahwa setiap kali saya melanggar disiplin menulis satu tes pada suatu waktu, segalanya menjadi buruk. Ini jebakan yang mudah untuk dijebak. "Oh, metode itu sepele," pikirmu sendiri, "jadi aku akan menghapus dua tes terkait lainnya dan terus bergerak." Nah, coba tebak? Tidak ada yang sepele seperti yang terlihat. Setiap kali saya jatuh ke dalam perangkap ini, saya akhirnya men-debug sesuatu yang saya pikir mudah, tetapi ternyata memiliki kasus sudut aneh. Dan karena saya terus menulis beberapa tes sekaligus, itu banyak pekerjaan untuk melacak di mana bug itu.

Jika Anda membutuhkan kumpulan informasi, Anda memiliki banyak opsi:

  • Papan tulis
  • Cerita pengguna
  • Komentar
  • Pena dan kertas yang bagus

Perhatikan bahwa tidak ada dalam daftar ini adalah kompiler. :-)

Kristo
sumber
5

Anda berasumsi bahwa Anda tahu seperti apa kode Anda sebelum Anda menulisnya. TDD / BDD adalah proses desain / penemuan sebanyak proses QA. Untuk fitur yang diberikan Anda menulis tes paling sederhana yang akan memverifikasi bahwa fitur tersebut puas (kadang-kadang ini mungkin memerlukan beberapa karena kompleksitas fitur). Tes pertama yang Anda tulis dimuat dengan asumsi seperti apa kode kerja itu nantinya. Jika Anda menulis seluruh rangkaian pengujian sebelum menulis baris kode pertama untuk mendukungnya, Anda membuat litani asumsi yang tidak diverifikasi. Sebagai gantinya, tulis satu asumsi dan verifikasi. Kemudian tulis selanjutnya. Dalam proses memverifikasi asumsi berikutnya, Anda mungkin saja mematahkan asumsi sebelumnya sehingga Anda harus kembali dan mengubah asumsi pertama untuk mencocokkan kenyataan atau mengubah realitas sehingga asumsi pertama masih berlaku.

Pikirkan setiap unit tes yang Anda tulis sebagai teori dalam buku catatan ilmiah. Ketika Anda mengisi buku catatan itu, Anda membuktikan teori Anda dan membentuk yang baru. Terkadang membuktikan teori baru membantah teori sebelumnya sehingga Anda harus memperbaikinya. Lebih mudah untuk membuktikan satu teori sekaligus daripada mencoba membuktikan katakan 20 sekaligus.

Michael Brown
sumber
TDD mengasumsikan Anda tahu seperti apa kode Anda sebelum Anda menulisnya juga, hanya dalam ukuran yang lebih kecil.
Michael Shaw
4

TDD adalah pendekatan yang sangat iteratif, yang (dalam pengalaman saya) lebih cocok dengan cara pembangunan dunia nyata. Biasanya implementasi saya terbentuk secara bertahap selama proses ini, dan setiap langkah dapat membawa pertanyaan, wawasan, dan ide untuk pengujian lebih lanjut. Ini sangat ideal untuk menjaga pikiran saya tetap fokus pada tugas yang sebenarnya, dan sangat efisien karena saya hanya perlu menyimpan beberapa hal dalam memori jangka pendek kapan saja. Ini pada gilirannya mengurangi kemungkinan kesalahan.

Ide Anda pada dasarnya adalah pendekatan Big Test Up Front, yang IMHO lebih sulit ditangani, dan mungkin menjadi lebih boros. Bagaimana jika Anda menyadari di tengah-tengah pekerjaan Anda bahwa pendekatan Anda tidak baik, API Anda cacat dan Anda harus memulai dari awal, atau menggunakan perpustakaan pihak ke-3? Kemudian banyak pekerjaan menulis tes Anda di depan menjadi usaha yang sia-sia.

Yang mengatakan, jika ini berhasil untuk Anda, baiklah. Saya dapat membayangkan bahwa jika Anda bekerja dari spesifikasi teknis tetap, terperinci, pada domain yang Anda alami dengan akrab, dan / atau pada tugas yang cukup kecil, Anda mungkin memiliki sebagian besar atau semua kasus uji yang diperlukan siap dan implementasi Anda jelas langsung dari permulaan. Maka mungkin masuk akal untuk memulai dengan menulis semua tes sekaligus. Jika pengalaman Anda adalah bahwa ini membuat Anda lebih produktif dalam jangka panjang, Anda tidak perlu terlalu khawatir tentang buku peraturan :-)

Péter Török
sumber
4

Selain memikirkan satu hal, satu paradigma TDD adalah menulis kode seminimal mungkin untuk lulus ujian. Ketika Anda menulis satu tes pada satu waktu, itu jauh lebih mudah untuk melihat jalan untuk menulis kode yang cukup untuk mendapatkan tes untuk lulus. Dengan serangkaian tes yang harus dilalui, Anda tidak perlu melihat kode dalam langkah-langkah kecil tetapi harus melakukan lompatan besar untuk membuat semuanya lulus sekaligus.

Sekarang jika Anda tidak membatasi diri untuk menulis kode untuk membuat semuanya lulus "dalam sekali jalan," melainkan menulis kode yang cukup untuk melewati satu tes pada suatu waktu, itu mungkin masih berfungsi. Anda harus memiliki lebih banyak disiplin untuk tidak hanya melanjutkan dan menulis lebih banyak kode daripada yang Anda butuhkan. Setelah Anda mulai dari jalur itu, Anda membiarkan diri Anda terbuka untuk menulis lebih banyak kode daripada tes yang dijelaskan, yang dapat diuji , setidaknya dalam arti bahwa itu tidak didorong oleh tes dan mungkin dalam arti bahwa itu tidak diperlukan (atau dilakukan) dengan tes apa pun.

Turun apa yang harus dilakukan metode ini, seperti komentar, cerita, spesifikasi fungsional, dll, sangat bisa diterima. Saya akan menunggu untuk menerjemahkan ini menjadi tes satu per satu.

Hal lain yang dapat Anda lewatkan dengan menulis tes sekaligus adalah proses berpikir dengan lulus ujian dapat mendorong Anda untuk memikirkan kasus uji lainnya. Tanpa bank tes yang ada, Anda perlu memikirkan kasus uji berikutnya dalam konteks tes kelulusan terakhir. Seperti yang saya katakan, memiliki ide bagus tentang apa yang seharusnya dilakukan metode ini sangat baik, tetapi berkali-kali saya menemukan diri saya menemukan kemungkinan-kemungkinan baru yang saya tidak anggap sebagai apriori, tetapi yang hanya terjadi dalam proses penulisan tes. Ada bahaya bahwa Anda mungkin melewatkan ini kecuali Anda secara khusus membiasakan diri memikirkan tes baru apa yang dapat saya tulis yang belum saya miliki.

tvanfosson
sumber
3

Saya telah bekerja pada sebuah proyek di mana pengembang yang menulis tes (gagal) berbeda dari pengembang yang menerapkan kode yang diperlukan untuk membuatnya lulus dan saya menemukan itu benar-benar efektif.

Dalam hal ini, hanya tes yang terkait dengan iterasi saat ini yang ditulis satu kali. Jadi apa yang Anda sarankan sangat mungkin dalam skenario semacam itu.

user2567
sumber
2
  • Kemudian Anda mencoba berkonsentrasi pada terlalu banyak hal sekaligus.
  • Saat menerapkan untuk membuat semua tes lulus, Anda tidak memiliki versi aplikasi yang berfungsi. Jika Anda harus mengimplementasikan banyak hal maka Anda tidak akan memiliki versi yang berfungsi dalam waktu yang lama.
magomi
sumber
2

Siklus Red-Green-Refactor adalah daftar periksa yang diperuntukkan bagi pengembang yang baru mengenal TDD. Saya akan mengatakan itu adalah ide yang baik untuk mengikuti daftar periksa ini sampai Anda tahu kapan harus mengikutinya dan kapan Anda bisa memecahkannya (yaitu, sampai tahu tidak harus menanyakan pertanyaan ini pada stackoverflow :)

Setelah melakukan TDD selama hampir satu dekade saya dapat memberitahu Anda bahwa saya sangat jarang, jika pernah, menulis banyak tes gagal sebelum saya menulis kode produksi.

Torbjörn Kalin
sumber
1

Anda menggambarkan BDD, di mana beberapa pemangku kepentingan eksternal memiliki spesifikasi yang dapat dieksekusi. Ini mungkin bermanfaat jika ada spesifikasi di muka yang telah ditentukan sebelumnya (misalnya spesifikasi format, standar industri atau di mana programmer bukan pakar domain).

Pendekatan normal kemudian secara bertahap mencakup semakin banyak tes penerimaan, yang merupakan kemajuan yang terlihat oleh manajer proyek dan pelanggan.

Anda biasanya memiliki tes ini ditentukan dan dieksekusi dalam kerangka BDD seperti Mentimun, Kebugaran atau semacamnya.

Namun, ini bukan sesuatu yang Anda campur dengan unit test Anda, yang jauh lebih dekat dengan detail implementasi yang rumit dengan banyak kasus tepi terkait API, masalah inisialisasi dll sangat terfokus pada item yang sedang diuji , yang merupakan artefak implementasi .

Disiplin red-green-refactor memiliki banyak manfaat, dan satu-satunya keuntungan Anda dapat berharap dengan mengetikkannya di depan adalah untuk mencapai titik impas.

Tormod
sumber
1

Satu tes sekaligus: keunggulan utama adalah fokus pada satu hal. Pikirkan desain kedalaman-pertama: Anda bisa lebih dalam dan tetap fokus dengan putaran umpan balik cepat. Anda mungkin melewatkan ruang lingkup seluruh masalah! Itulah saat refactoring (besar) ikut berperan. Tanpanya TDD tidak berfungsi.

Semua tes: analisis dan perancangan dapat mengungkapkan lebih banyak ruang lingkup masalah Anda. Pikirkan desain luas pertama. Anda menganalisis masalah dari sudut yang lebih banyak dan menambahkan masukan dari pengalaman. Secara inheren lebih sulit, tetapi dapat menghasilkan manfaat yang menarik - lebih sedikit refactoring - jika Anda melakukan 'cukup banyak saja'. Berhati-hatilah, mudah untuk dianalisa secara berlebihan dan benar-benar meleset!

Saya merasa sulit untuk secara umum merekomendasikan untuk memilih satu atau yang lain, karena banyak faktor: pengalaman (terutama dengan masalah yang sama), pengetahuan dan keterampilan domain, keramahan kode untuk refactoring, kompleksitas masalah ...

Saya kira jika kita fokus lebih sempit ke aplikasi bisnis biasa maka TDD dengan pendekatan coba-coba & kesalahan paling cepat biasanya akan menang dalam hal efektivitas.

Merusak
sumber
1

Dengan asumsi bahwa kerangka pengujian Anda mendukungnya, apa yang saya sarankan adalah alih-alih mengimplementasikan tes yang ingin Anda braindump, alih-alih tulis tes pending deskriptif yang nantinya akan Anda implementasikan. Misalnya, jika API Anda harus melakukan foo dan bar tetapi tidak biz, cukup tambahkan kode berikut (contoh ini di rspec) untuk suite pengujian Anda, lalu serang mereka satu per satu. Anda menurunkan pikiran Anda dengan cepat dan dapat mengatasi semua masalah Anda satu per satu. Ketika semua tes lulus Anda akan tahu kapan Anda telah mengatasi semua masalah yang Anda miliki selama braindump Anda.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
Tebusan Briggs
sumber