Kapan Anda menulis kode "asli" dalam TDD?

147

Semua contoh yang saya baca dan lihat di video pelatihan memiliki contoh sederhana. Tapi apa yang saya tidak lihat jika bagaimana saya melakukan kode "nyata" setelah saya mendapatkan hijau. Apakah ini bagian "Refactor"?

Jika saya memiliki objek yang cukup kompleks dengan metode yang kompleks, dan saya menulis tes dan minimum untuk membuatnya lulus (setelah itu pertama kali gagal, Red). Kapan saya kembali dan menulis kode asli? Dan berapa banyak kode nyata yang harus saya tulis sebelum saya tes ulang? Saya menduga yang terakhir lebih intuisi.

Sunting: Terima kasih kepada semua yang menjawab. Semua jawaban Anda sangat membantu saya. Tampaknya ada berbagai ide tentang apa yang saya tanyakan atau bingung, dan mungkin ada, tetapi yang saya tanyakan adalah, katakan saya memiliki aplikasi untuk membangun sekolah.

Dalam desain saya, saya memiliki arsitektur yang ingin saya mulai dengan, Cerita Pengguna, dan sebagainya. Dari sini, saya mengambil Cerita Pengguna tersebut, dan saya membuat tes untuk menguji Kisah Pengguna. Pengguna mengatakan, Kami memiliki orang yang mendaftar ke sekolah dan membayar biaya pendaftaran. Jadi, saya memikirkan cara untuk membuatnya gagal. Dengan melakukan itu saya merancang Tes kelas untuk kelas X (mungkin Siswa), yang akan gagal. Saya kemudian membuat kelas "Siswa." Mungkin "Sekolah" saya tidak tahu.

Tapi, bagaimanapun, Desain TD memaksa saya untuk memikirkan cerita. Jika saya dapat membuat tes gagal, saya tahu mengapa itu gagal, tetapi ini mengandaikan saya bisa lulus. Ini tentang mendesain.

Saya menyamakan ini dengan berpikir tentang Rekursi. Rekursi bukanlah konsep yang sulit. Mungkin lebih sulit untuk benar-benar melacaknya di kepala Anda, tetapi pada kenyataannya, bagian tersulit adalah mengetahui, ketika rekursi "pecah," kapan harus berhenti (pendapat saya, tentu saja.) Jadi saya harus memikirkan apa yang berhenti Rekursi pertama. Ini hanya analogi yang tidak sempurna, dan mengasumsikan bahwa setiap iterasi rekursif adalah "pass." Sekali lagi, hanya sebuah opini.

Dalam implementasinya, sekolah lebih sulit dilihat. Buku besar numerik dan perbankan "mudah" dalam arti Anda dapat menggunakan aritmatika sederhana. Saya dapat melihat a + b dan mengembalikan 0, dll. Dalam hal sistem orang, saya harus berpikir lebih keras tentang bagaimana mengimplementasikannya . Saya memiliki konsep gagal, lulus, refactor (kebanyakan karena studi dan pertanyaan ini.)

Apa yang saya tidak tahu didasarkan pada kurangnya pengalaman, menurut pendapat saya. Saya tidak tahu bagaimana cara gagal mendaftar siswa baru. Saya tidak tahu bagaimana cara seseorang gagal mengetik nama belakang dan itu disimpan ke database. Saya tahu cara membuat +1 untuk matematika sederhana, tetapi dengan entitas seperti orang, saya tidak tahu apakah saya hanya menguji untuk melihat apakah saya mendapatkan kembali ID unik basis data atau sesuatu yang lain ketika seseorang memasukkan nama dalam sebuah database atau keduanya atau tidak.

Atau, mungkin ini menunjukkan saya masih bingung.

johnny
sumber
193
Setelah TDD orang-orang pulang untuk bermalam.
hobbs
14
Menurut Anda mengapa kode yang Anda tulis tidak nyata?
Goyo
2
@RubberDuck Lebih dari jawaban lainnya. Saya yakin saya akan merujuknya segera. Ini masih asing, tetapi saya tidak akan menyerah. Apa yang Anda katakan masuk akal. Saya hanya mencoba membuatnya masuk akal dalam konteks saya atau aplikasi bisnis biasa. Mungkin sistem inventaris atau sejenisnya. Saya harus mempertimbangkannya. Saya berterima kasih atas waktu Anda. Terima kasih.
johnny
1
Jawabannya sudah menyentuh kepala, tetapi selama semua tes Anda berlalu, dan Anda tidak memerlukan tes / fungsi baru, dapat diasumsikan kode yang Anda miliki sudah selesai, kecuali linting.
ESR
3
Ada asumsi dalam pertanyaan yang mungkin bermasalah dalam "Saya punya objek yang cukup kompleks dengan metode yang kompleks". Di TDD Anda menulis tes Anda terlebih dahulu sehingga Anda mulai dengan kode yang cukup sederhana. Ini akan memaksa Anda untuk membuat kode struktur yang ramah pengujian yang harus modular. Jadi perilaku kompleks akan dibuat dengan menggabungkan objek yang lebih sederhana. Jika Anda berakhir dengan objek atau metode yang cukup rumit maka adalah ketika Anda refactor
Borjab

Jawaban:

243

Jika saya memiliki objek yang cukup kompleks dengan metode yang kompleks, dan saya menulis tes dan minimum untuk membuatnya lulus (setelah itu pertama kali gagal, Red). Kapan saya kembali dan menulis kode asli? Dan berapa banyak kode nyata yang harus saya tulis sebelum saya tes ulang? Saya menduga yang terakhir lebih intuisi.

Anda tidak "kembali" dan menulis "kode nyata". Itu semua kode nyata. Apa yang Anda lakukan adalah kembali dan menambahkan tes lain yang memaksa Anda untuk mengubah kode Anda untuk membuat lulus ujian baru.

Adapun berapa banyak kode yang Anda tulis sebelum diuji ulang? Tidak ada Anda menulis kode nol tanpa gagal tes yang memaksa Anda untuk menulis lebih banyak kode.

Perhatikan polanya?

Mari kita telusuri (contoh lain) sederhana dengan harapan bisa membantu.

Assert.Equal("1", FizzBuzz(1));

Pei mudah.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Bukan apa yang Anda sebut kode nyata, bukan? Mari kita tambahkan tes yang memaksa perubahan.

Assert.Equal("2", FizzBuzz(2));

Kita bisa melakukan sesuatu yang konyol if n == 1, tetapi kita akan beralih ke solusi yang waras.

public String FizzBuzz(int n) {
    return n.ToString();
}

Keren. Ini akan berfungsi untuk semua nomor non-FizzBuzz. Apa masukan selanjutnya yang akan memaksa kode produksi untuk berubah?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

Dan lagi. Tulis tes yang belum lulus.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

Dan kami sekarang telah membahas semua kelipatan tiga (yang tidak juga kelipatan lima, kami akan mencatatnya dan kembali).

Kami belum menulis tes untuk "Buzz", jadi mari kita tulis itu.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

Dan lagi, kita tahu ada kasus lain yang perlu kita tangani.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

Dan sekarang kita bisa menangani semua kelipatan 5 yang tidak kelipatan 3.

Sampai saat ini, kami telah mengabaikan langkah refactoring, tetapi saya melihat beberapa duplikasi. Mari kita bersihkan sekarang.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Keren. Sekarang kami telah menghapus duplikasi dan membuat fungsi yang bernama baik. Apa tes selanjutnya yang dapat kita tulis yang akan memaksa kita untuk mengubah kode? Yah, kita telah menghindari kasus di mana angka itu dapat dibagi oleh 3 dan 5. Mari kita tulis sekarang.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Tes lulus, tetapi kami memiliki lebih banyak duplikasi. Kami memiliki opsi, tetapi saya akan menerapkan "Ekstrak Variabel Lokal" beberapa kali sehingga kami melakukan refactoring alih-alih menulis ulang.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Dan kami telah membahas setiap input yang masuk akal, tetapi bagaimana dengan input yang tidak masuk akal ? Apa yang terjadi jika kita melewati 0 atau negatif? Tulis kasus uji itu.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Apakah ini mulai terlihat seperti "kode nyata"? Lebih penting lagi, pada titik mana ia berhenti menjadi "kode tidak nyata" dan transisi menjadi "nyata"? Itu sesuatu untuk direnungkan ...

Jadi, saya bisa melakukan ini hanya dengan mencari tes yang saya tahu tidak akan lulus pada setiap langkah, tetapi saya sudah banyak berlatih. Ketika saya sedang bekerja, banyak hal tidak sesederhana ini dan saya mungkin tidak selalu tahu tes apa yang akan memaksa perubahan. Terkadang saya akan menulis tes dan terkejut melihatnya sudah lulus! Saya sangat menyarankan agar Anda terbiasa membuat "Daftar Tes" sebelum memulai. Daftar tes ini harus berisi semua input "menarik" yang dapat Anda pikirkan. Anda mungkin tidak menggunakan semuanya dan Anda kemungkinan akan menambahkan kasus saat bepergian, tetapi daftar ini berfungsi sebagai peta jalan. Daftar tes saya untuk FizzBuzz akan terlihat seperti ini.

  • Negatif
  • Nol
  • Satu
  • Dua
  • Tiga
  • Empat
  • Lima
  • Enam (kelipatan non trivial 3)
  • Sembilan (3 kuadrat)
  • Sepuluh (kelipatan non sepele dari 5)
  • 15 (kelipatan 3 & 5)
  • 30 (kelipatan non sepele dari 3 & 5)
Bebek karet
sumber
3
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft
47
Kecuali saya benar-benar salah paham jawaban ini: "Kita bisa melakukan sesuatu yang konyol seperti jika n == 1, tetapi kita akan beralih ke solusi yang waras." - Semuanya konyol. Jika Anda tahu sebelumnya bahwa Anda menginginkan fungsi yang melakukan <spec>, tulis tes untuk <spec> dan lewati bagian tempat Anda menulis versi yang jelas-jelas gagal <spec>. Jika Anda menemukan bug di <spec> maka yakinlah: tulis tes terlebih dahulu untuk memverifikasi Anda bisa menggunakannya sebelum perbaikan dan mengamati lulus ujian setelah perbaikan. Tetapi tidak perlu memalsukan semua langkah perantara ini.
GManNickG
16
Komentar yang menunjukkan kelemahan utama dalam jawaban ini dan TDD secara umum telah dipindahkan ke obrolan. Jika Anda mempertimbangkan untuk menggunakan TDD, harap baca 'obrolan'. Sayangnya, komentar 'berkualitas' sekarang disembunyikan di antara banyak obrolan untuk dibaca oleh para calon siswa.
user3791372
2
@ GMNickG Saya percaya intinya adalah untuk mendapatkan jumlah tes yang tepat. Menulis tes sebelumnya membuatnya mudah untuk melewatkan kasus-kasus khusus apa yang harus diuji, yang mengarah ke situasi yang tidak tercakup secara memadai dalam tes, atau pada dasarnya situasi yang sama tanpa batas dibahas berkali-kali dalam tes. Jika Anda bisa melakukannya tanpa langkah-langkah perantara ini, hebat! Tidak semua orang bisa melakukannya, itu adalah sesuatu yang membutuhkan latihan.
hvd
1
Dan inilah kutipan dari Kent Beck tentang refactoring: "Sekarang setelah tes berjalan, kita dapat menyadari (seperti dalam" membuat nyata ") implementasi ringkasan ()". Dia kemudian mulai mengubah konstanta ke variabel. Saya merasa kutipan ini cocok dengan pertanyaan itu.
Chris Wohlert
46

Kode "asli" adalah kode yang Anda tulis untuk membuat test pass Anda. Sungguh . Sesederhana itu.

Ketika orang berbicara tentang menulis minimum untuk membuat tes hijau, itu hanya berarti bahwa kode asli Anda harus mengikuti prinsip YAGNI .

Gagasan langkah refactor hanya untuk membersihkan apa yang telah Anda tulis begitu Anda senang bahwa itu memenuhi persyaratan.

Selama tes yang Anda tulis benar-benar mencakup persyaratan produk Anda, begitu mereka lulus maka kode selesai. Pikirkan tentang hal ini, jika semua persyaratan bisnis Anda memiliki tes dan semua tes itu hijau, apa lagi yang harus ditulis? (Oke, dalam kehidupan nyata kita cenderung tidak memiliki cakupan tes lengkap, tetapi teorinya sehat.)

GenericJon
sumber
45
Tes unit tidak dapat benar-benar mencakup persyaratan produk Anda bahkan untuk persyaratan yang relatif sepele sekalipun. Paling-paling, mereka sampel ruang input-output dan idenya adalah bahwa Anda (benar) menggeneralisasi ke ruang input-output penuh. Tentu saja, kode Anda bisa menjadi besar switchdengan kasing untuk setiap tes unit yang akan lulus semua tes dan gagal untuk input lainnya.
Derek Elkins
8
@DerekElkins TDD mengamanatkan tes gagal. Tidak gagal tes unit.
Taemyr
6
@DerekElkins itu sebabnya Anda tidak hanya menulis unit test, dan juga mengapa ada asumsi umum bahwa Anda mencoba membuat sesuatu bukan hanya memalsukannya!
jonrsharpe
36
@jonrsharpe Dengan logika itu, saya tidak akan pernah menulis implementasi sepele. Misalnya dalam contoh FizzBuzz dalam jawaban RubberDuck (yang hanya menggunakan tes unit), implementasi pertama jelas "hanya memalsukannya". Pemahaman saya atas pertanyaan ini adalah dikotomi antara menulis kode yang Anda tahu tidak lengkap dan kode yang Anda benar-benar percaya akan menerapkan persyaratan, "kode nyata". "Besar switch" saya dimaksudkan sebagai ekstrem logis dari "menulis minimum untuk membuat tes hijau". Saya melihat pertanyaan OP sebagai: di mana dalam TDD adalah prinsip yang menghindari sebesar ini switch?
Derek Elkins
2
@GenericJon Itu agak terlalu optimis dalam pengalaman saya :) Untuk satu, ada orang yang menikmati pekerjaan yang berulang-ulang tanpa alasan. Mereka akan lebih bahagia dengan pernyataan peralihan raksasa daripada dengan "pengambilan keputusan yang rumit". Dan untuk kehilangan pekerjaan mereka, mereka akan membutuhkan seseorang yang memanggil mereka dengan teknik tersebut (dan mereka lebih baik memiliki bukti yang baik bahwa sebenarnya kehilangan peluang perusahaan / uang!), Atau melakukan yang sangat buruk. Setelah mengambil alih pemeliharaan pada banyak proyek seperti itu, saya dapat mengatakan bahwa mudah bagi kode yang sangat naif untuk bertahan selama beberapa dekade, selama itu membuat pelanggan senang (dan membayar).
Luaan
14

Jawaban singkatnya adalah "kode asli" adalah kode yang membuat lulus ujian. Jika Anda dapat lulus tes dengan sesuatu selain kode nyata, tambahkan lebih banyak tes!

Saya setuju bahwa banyak tutorial tentang TDD sederhana. Itu bekerja melawan mereka. Tes yang terlalu sederhana untuk metode yang, misalnya, menghitung 3 + 8 benar-benar tidak punya pilihan selain juga menghitung 3 + 8 dan membandingkan hasilnya. Itu membuatnya tampak seperti Anda hanya akan menduplikasi kode di seluruh, dan pengujian itu sia-sia, rawan kesalahan pekerjaan tambahan.

Ketika Anda mahir dalam pengujian, itu akan menginformasikan bagaimana Anda menyusun aplikasi Anda, dan bagaimana Anda menulis kode Anda. Jika Anda mengalami masalah dengan tes yang masuk akal dan membantu, Anda mungkin harus memikirkan kembali desain Anda sedikit. Sistem yang dirancang dengan baik mudah untuk diuji - yang berarti tes masuk akal mudah dipikirkan, dan diimplementasikan.

Ketika Anda menulis tes Anda terlebih dahulu, perhatikan gagal, dan kemudian tulis kode yang membuatnya lulus, itu adalah disiplin untuk memastikan bahwa semua kode Anda memiliki tes yang sesuai. Saya tidak dengan patuh mengikuti aturan itu ketika saya sedang coding; sering saya menulis tes setelah fakta. Tetapi melakukan tes terlebih dahulu membantu membuat Anda jujur. Dengan beberapa pengalaman, Anda akan mulai memperhatikan ketika Anda mengkodekan diri sendiri ke sudut, bahkan ketika Anda tidak menulis tes terlebih dahulu.

Carl Raymond
sumber
6
Secara pribadi, ujian yang saya tulis adalah assertEqual(plus(3,8), 11), bukan assertEqual(plus(3,8), my_test_implementation_of_addition(3,8)). Untuk kasus yang lebih kompleks, Anda selalu mencari cara untuk membuktikan hasil yang benar, selain secara dinamis menghitung hasil yang benar dalam tes dan memeriksa kesetaraan.
Steve Jessop
Jadi untuk cara yang sangat konyol melakukannya untuk contoh ini, Anda mungkin membuktikan bahwa plus(3,8)telah mengembalikan hasil yang benar dengan mengurangi 3 dari itu, mengurangi 8 dari itu, dan memeriksa hasilnya terhadap 0. Ini sangat jelas sama assertEqual(plus(3,8), 3+8)dengan menjadi Agak absurd, tetapi jika kode yang diuji membangun sesuatu yang lebih rumit dari sekedar integer, maka mengambil hasilnya dan memeriksa setiap bagian untuk kebenaran seringkali merupakan pendekatan yang tepat. Atau, sesuatu sepertifor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
Steve Jessop
... karena itu menghindari ketakutan besar, yaitu ketika menulis tes kita akan membuat kesalahan yang sama pada subjek "bagaimana menambahkan 10" yang kita buat dalam kode langsung. Jadi tes ini dengan hati-hati menghindari penulisan kode apa pun yang menambahkan 10 untuk apa pun, dalam tes yang plus()dapat menambahkan 10 untuk hal-hal. Kami masih mengandalkan nilai loop awal yang diverifikasi programmer, tentu saja.
Steve Jessop
4
Hanya ingin menunjukkan bahwa bahkan jika Anda menulis tes setelah fakta, itu masih ide yang baik untuk melihat mereka gagal; temukan beberapa bagian dari kode yang tampaknya penting untuk apa pun yang sedang Anda kerjakan, sesuaikan sedikit (mis. ganti + dengan a -, atau apa pun), jalankan tes dan lihat gagal, batalkan perubahan dan lihat mereka lulus. Berkali-kali saya melakukan ini, tesnya tidak benar-benar gagal, membuatnya lebih buruk daripada tidak berguna: tidak hanya itu tidak menguji apa pun, itu memberi saya kepercayaan palsu bahwa ada sesuatu yang sedang diuji!
Warbo
6

Terkadang beberapa contoh tentang TDD bisa menyesatkan. Seperti yang ditunjukkan orang lain sebelumnya, kode yang Anda tulis untuk membuat tes lulus adalah kode asli.

Tetapi jangan berpikir bahwa kode yang sebenarnya tampak seperti sihir - itu salah. Anda membutuhkan pemahaman yang lebih baik tentang apa yang ingin Anda capai dan kemudian Anda harus memilih tes yang sesuai, mulai dari kasing termudah dan kasing sudut.

Misalnya, jika Anda perlu menulis lexer, Anda mulai dengan string kosong, kemudian dengan sekelompok spasi putih, lalu angka, lalu dengan angka yang dikelilingi oleh spasi putih, lalu angka yang salah, dll. Transformasi kecil ini akan mengarahkan Anda ke algoritme yang tepat, tetapi Anda tidak beralih dari kasing termudah ke kasing rumit yang dipilih dengan bodoh untuk menyelesaikan kode sebenarnya.

Bob Martin menjelaskannya dengan sempurna di sini .

Victor Cejudo
sumber
5

Bagian refactor dibersihkan ketika Anda lelah dan ingin pulang.

Saat Anda akan menambahkan fitur, bagian refactor adalah apa yang Anda ubah sebelum tes berikutnya. Anda membuat ulang kode untuk memberi ruang bagi fitur baru. Anda melakukan ini ketika Anda tahu apa yang akan menjadi fitur baru itu. Tidak ketika Anda hanya membayangkannya.

Hal ini dapat yang sederhana seperti mengubah nama GreetImpluntuk GreetWorldsebelum Anda membuat GreetMomkelas (setelah menambahkan test) untuk menambahkan fitur yang akan mencetak "Hi Mom".

candied_orange
sumber
1

Tetapi kode sebenarnya akan muncul pada tahap refactor dari fase TDD. Yakni kode yang harus menjadi bagian dari rilis final.

Tes harus dijalankan setiap kali Anda membuat perubahan.

Moto siklus hidup TDD adalah: RED GREEN REFACTOR

MERAH : Tulis tesnya

HIJAU : Berusaha jujur ​​untuk mendapatkan kode fungsional yang lulus tes secepat mungkin: kode duplikat, nama variabel yang diretas dengan urutan paling tinggi, dll.

REFACTOR : Bersihkan kode, beri nama variabel dengan benar. KERING kodenya.

graeme
sumber
6
Saya tahu apa yang Anda katakan tentang fase "Hijau" tetapi itu menyiratkan bahwa nilai pengembalian kabel untuk membuat tes lulus mungkin tepat. Dalam pengalaman saya, "Hijau" harus menjadi upaya jujur ​​untuk membuat kode kerja untuk memenuhi persyaratan, mungkin tidak sempurna tetapi harus selengkap dan "dapat dikirim" seperti yang dapat dikelola pengembang di pas pertama. Refactoring mungkin sebaiknya dilakukan beberapa waktu kemudian setelah Anda melakukan lebih banyak pengembangan dan masalah dengan pass pertama menjadi lebih jelas dan peluang untuk KERING muncul.
mcottle
2
@ mcottle: Anda mungkin terkejut berapa banyak implementasi dari repositori get-only yang bisa berupa nilai-nilai hardcoded dalam basis kode. :)
Bryan Boettcher
6
Mengapa saya pernah menulis kode omong kosong dan membersihkannya, ketika saya bisa mengeluarkan kode kualitas produksi yang bagus hampir secepat saya bisa mengetik? :)
Kaz
1
@Kaz Karena dengan cara ini Anda berisiko menambah perilaku yang tidak teruji . Satu-satunya cara untuk memastikan memiliki tes untuk masing-masing dan setiap perilaku yang diinginkan adalah melakukan perubahan sesederhana mungkin terlepas dari seberapa buruk itu. Terkadang refactoring berikut memunculkan pendekatan baru yang tidak Anda pikirkan sebelumnya ...
Timothy Truckle
1
@TimothyTruckle Bagaimana jika butuh 50 menit untuk menemukan perubahan yang paling sederhana, tetapi hanya 5 untuk menemukan perubahan kedua yang paling sederhana? Apakah kita menggunakan yang paling sederhana kedua atau terus mencari yang paling sederhana?
Kaz
1

Kapan Anda menulis kode "asli" dalam TDD?

The merah fase adalah di mana Anda menulis kode.

Pada fase refactoring , tujuan utamanya adalah menghapus kode.

Pada fase merah Anda melakukan apa saja untuk membuat tes lulus secepat mungkin dan dengan biaya berapa pun . Anda sama sekali mengabaikan apa yang pernah Anda dengar tentang praktik pengkodean yang baik atau pola desain yang sama. Membuat tes hijau adalah yang terpenting.

Pada fase refactoring Anda membersihkan kekacauan yang baru saja Anda buat. Sekarang pertama-tama Anda melihat apakah perubahan yang baru saja Anda buat adalah jenis yang paling atas dalam daftar Prioritas Transformasi dan jika ada duplikasi kode yang paling mungkin Anda hapus dengan menerapkan patter desain.

Akhirnya Anda meningkatkan keterbacaan dengan mengganti nama pengidentifikasi dan mengekstraksi angka ajaib dan / atau string literal ke konstanta.


Itu bukan red-refactor, itu red-green-refactor. - Rob Kinyon

Terima kasih telah menunjukkan ini.

Jadi itu adalah fase hijau di mana Anda menulis kode asli

Pada fase merah Anda menulis spesifikasi yang dapat dieksekusi ...

Timothy Truckle
sumber
Itu bukan red-refactor, itu red-green-refactor. "Merah" adalah Anda mengambil suite tes Anda dari hijau (semua tes lulus) menjadi merah (satu tes gagal). "Hijau" adalah tempat Anda dengan sembarangan mengambil suite tes Anda dari merah (satu tes gagal) menjadi hijau (semua tes lulus). "Refactor" adalah tempat Anda mengambil kode dan membuatnya cantik sambil menjaga semua tes tetap berlalu.
Rob Kinyon
1

Anda menulis Kode Nyata sepanjang waktu.

Pada setiap langkah, Anda menulis kode untuk memenuhi persyaratan yang akan dipenuhi oleh kode Anda untuk penelepon berikutnya di kode Anda (yang mungkin Anda atau tidak ...).

Anda pikir Anda tidak sedang menulis kode yang berguna ( nyata ), karena suatu saat Anda mungkin akan menolaknya.

Code-Refactoring adalah proses merestrukturisasi kode komputer yang ada — mengubah anjak piutang — tanpa mengubah perilaku eksternalnya.

Apakah ini berarti bahwa meskipun Anda mengubah kode, kondisi kode satisified, dibiarkan tidak berubah. Dan pemeriksaan ( tes ) yang Anda laksanakan untuk memverifikasi kode Anda sudah ada untuk memverifikasi apakah modifikasi Anda mengubah apa pun. Jadi kode yang Anda tulis sepanjang waktu ada di sana, hanya dengan cara yang berbeda.

Alasan lain Anda mungkin berpikir bahwa itu bukan kode asli, adalah karena Anda sedang melakukan contoh di mana program akhir sudah dapat Anda lihat sebelumnya. Ini sangat bagus, karena ini menunjukkan Anda memiliki pengetahuan tentang domain tempat Anda pemrograman.
Tetapi seringkali programmer berada dalam domain yang baru , tidak diketahui oleh mereka. Mereka tidak tahu apa hasil akhirnya dan TDD adalah teknik untuk menulis program langkah demi langkah, mendokumentasikan pengetahuan kita tentang bagaimana sistem ini harus bekerja dan memverifikasi bahwa kode kita berfungsi seperti itu.

Ketika saya membaca The Book (*) di TDD, bagi saya fitur terpenting yang menonjol adalah: Daftar TODO. Ini menunjukkan kepada saya bahwa, TDD juga merupakan teknik untuk membantu pengembang fokus pada satu hal pada suatu waktu. Jadi ini juga merupakan jawaban untuk pertanyaan Anda tentang berapa banyak kode nyata untuk ditulis ? Saya akan mengatakan kode yang cukup untuk fokus pada 1 hal sekaligus.

(*) "Pengembangan Didorong oleh Uji: Dengan Contoh" oleh Kent Beck

Robert Andrzantai
sumber
2
"Test Driven Development: By Example" oleh Kent Beck
Robert Andrzamanan
1

Anda tidak menulis kode untuk membuat tes Anda gagal.

Anda menulis tes untuk menentukan seperti apa seharusnya sukses itu, yang semestinya semuanya gagal karena Anda belum menulis kode yang akan lulus.

Inti dari menulis tes yang awalnya gagal adalah melakukan dua hal:

  1. Tutupi semua kasing - semua kasing nominal, semua kasing tepi, dll.
  2. Validasikan tes Anda. Jika Anda hanya pernah melihat mereka lulus, bagaimana Anda bisa yakin mereka akan melaporkan kegagalan ketika terjadi?

Poin di balik red-green-refactor adalah bahwa menulis tes yang benar terlebih dahulu memberi Anda kepercayaan diri untuk mengetahui bahwa kode yang Anda tulis untuk lulus tes adalah benar, dan memungkinkan Anda untuk refactor dengan keyakinan bahwa tes Anda akan memberi tahu Anda segera setelah sesuatu rusak, sehingga Anda dapat segera kembali dan memperbaikinya.

Dalam pengalaman saya sendiri (C # /. NET), tes murni pertama adalah sedikit ideal yang tidak dapat dicapai, karena Anda tidak dapat mengkompilasi panggilan ke metode yang belum ada. Jadi "test first" benar-benar tentang pengkodean antarmuka dan implementasi stubbing terlebih dahulu, kemudian menulis tes terhadap bertopik (yang awalnya akan gagal) sampai bertopik dengan benar. Saya tidak pernah menulis "kode gagal", hanya membangun dari bertopik.

Zenilogix
sumber
0

Saya pikir Anda mungkin bingung antara tes unit dan tes integrasi. Saya percaya mungkin ada juga tes penerimaan, tapi itu tergantung pada proses Anda.

Setelah Anda menguji semua "unit" kecil maka Anda menguji mereka semua dirakit, atau "terintegrasi." Itu biasanya seluruh program atau perpustakaan.

Dalam kode yang saya tulis, tes integrasi perpustakaan dengan berbagai program pengujian yang membaca data dan memasukkannya ke perpustakaan, lalu memeriksa hasilnya. Lalu saya melakukannya dengan utas. Lalu saya melakukannya dengan utas dan fork () di tengah. Kemudian saya menjalankannya dan mematikan -9 setelah 2 detik, kemudian saya memulainya dan memeriksa mode pemulihannya. Saya mengerjainya. Saya menyiksanya dengan berbagai cara.

Semua itu juga JUGA menguji, tetapi saya tidak memiliki tampilan yang cukup merah / hijau untuk hasilnya. Entah berhasil, atau saya menggali beberapa ribu baris kode kesalahan untuk mencari tahu mengapa.

Di situlah Anda menguji "kode nyata."

Dan saya hanya memikirkan hal ini, tetapi mungkin Anda tidak tahu kapan Anda seharusnya selesai menulis unit test. Anda selesai menulis unit test ketika tes Anda melakukan semua yang Anda tentukan harus dilakukan. Kadang-kadang Anda dapat kehilangan jejak itu di antara semua penanganan kesalahan dan tepi kasus, jadi Anda mungkin ingin membuat kelompok uji yang bagus dari tes jalur bahagia yang hanya langsung melalui spesifikasi.

Zan Lynx
sumber
(= posesif, ini = "ada" atau "sudah". Lihat misalnya Bagaimana Menggunakan dan Ini .)
Peter Mortensen
-6

Dalam menjawab judul pertanyaan: "Kapan Anda menulis kode" asli "dalam TDD?", Jawabannya adalah: 'hampir tidak pernah' atau 'sangat lambat'.

Anda terdengar seperti seorang siswa, jadi saya akan menjawab seolah-olah menasihati seorang siswa.

Anda akan belajar banyak pengkodean 'teori' dan 'teknik'. Mereka bagus untuk menghabiskan waktu pada kursus siswa yang terlalu mahal, tetapi sangat sedikit manfaatnya bagi Anda bahwa Anda tidak bisa membaca dalam sebuah buku setengah waktu.

Tugas seorang pembuat kode hanya untuk menghasilkan kode. Kode yang bekerja dengan sangat baik. Itulah sebabnya Anda, pembuat kode merencanakan kode dalam pikiran Anda, di atas kertas, dalam aplikasi yang sesuai, dll., Dan Anda berencana untuk mengatasi kemungkinan kelemahan / lubang di muka dengan berpikir secara logis dan lateral sebelum pengkodean.

Tetapi Anda perlu tahu cara memecah aplikasi Anda untuk dapat merancang kode yang layak. Misalnya, jika Anda tidak tahu tentang Little Bobby Table (xkcd 327), maka Anda mungkin tidak akan membersihkan input sebelum bekerja dengan database, sehingga Anda tidak akan dapat mengamankan data Anda di sekitar konsep itu.

TDD hanyalah alur kerja yang dirancang untuk meminimalkan bug dalam kode Anda dengan membuat tes tentang apa yang bisa salah sebelum Anda membuat kode aplikasi Anda karena pengkodean dapat menjadi sulit secara eksponensial semakin banyak kode yang Anda perkenalkan dan Anda lupa bug yang pernah Anda pikirkan. Setelah Anda berpikir bahwa Anda telah menyelesaikan aplikasi Anda, Anda menjalankan tes dan boom, semoga bug tertangkap dengan tes Anda.

TDD bukan - seperti yang diyakini sebagian orang - menulis tes, membuatnya lulus dengan kode minimal, menulis tes lain, mendapatkan lulus itu dengan kode minimal, dll. Sebaliknya, ini adalah cara membantu Anda membuat kode dengan percaya diri. Ideal kode refactoring berkelanjutan untuk membuatnya bekerja dengan tes adalah idiot, tetapi ini adalah konsep yang bagus di antara siswa karena membuat mereka merasa hebat ketika mereka menambahkan fitur baru dan mereka masih belajar cara membuat kode ...

Tolong jangan jatuh perangkap ini dan melihat peran Anda pengkodean untuk apa itu - pekerjaan seorang koder adalah hanya untuk menghasilkan kode. Kode yang bekerja dengan sangat baik. Sekarang, ingatlah Anda akan siap sebagai coder profesional, dan klien Anda tidak akan peduli jika Anda menulis 100.000 pernyataan, atau 0. Mereka hanya menginginkan kode yang berfungsi. Sebenarnya sangat bagus.

pengguna3791372
sumber
3
Saya bahkan tidak dekat dengan seorang siswa, tetapi saya membaca dan mencoba menerapkan teknik yang baik dan menjadi profesional. Jadi dalam pengertian itu, saya adalah seorang "murid." Saya hanya mengajukan pertanyaan yang sangat mendasar karena itulah saya. Saya ingin tahu persis mengapa saya melakukan apa yang saya lakukan. Jantung dari masalah ini. Jika saya tidak mendapatkan itu, saya tidak suka dan mulai mengajukan pertanyaan. Saya perlu tahu mengapa, jika saya akan menggunakannya. TDD tampaknya baik secara intuitif dalam beberapa hal seperti mengetahui apa yang Anda butuhkan untuk membuat dan memikirkan semuanya, tetapi implementasinya sulit dipahami. Saya pikir saya memiliki pemahaman yang lebih baik sekarang.
johnny
4
Itu adalah aturan TDD. Anda bebas menulis kode apa pun yang Anda inginkan, tetapi jika Anda tidak mengikuti ketiga aturan itu, Anda tidak melakukan TDD.
Sean Burton
2
"Aturan" satu orang? TDD adalah saran untuk membantu Anda membuat kode, bukan agama. Sungguh menyedihkan melihat begitu banyak orang mematuhi ide dengan sangat anal. Bahkan asal usul TDD masih kontroversial.
user3791372
2
@ user3791372 TDD adalah proses yang sangat ketat dan jelas. Bahkan jika banyak yang berpikir itu hanya berarti "Lakukan beberapa pengujian ketika Anda pemrograman", itu tidak benar. Mari kita coba untuk tidak mencampuradukkan istilah di sini, pertanyaan ini adalah tentang proses TDD, bukan pengujian umum.
Alex