Bagaimana cara mendapatkan API awal yang benar menggunakan TDD?


12

Ini mungkin pertanyaan yang agak konyol karena saya pada upaya pertama saya di TDD. Saya menyukai rasa percaya diri yang dibawanya dan struktur kode saya yang umumnya lebih baik tetapi ketika saya mulai menerapkannya pada sesuatu yang lebih besar dari contoh mainan satu kelas, saya mengalami kesulitan.

Misalkan, Anda sedang menulis semacam perpustakaan. Anda tahu apa yang harus dilakukan, Anda tahu cara umum bagaimana seharusnya diterapkan (arsitektur bijaksana), tetapi Anda terus "menemukan" bahwa Anda perlu membuat perubahan pada API publik Anda saat Anda kode. Mungkin Anda perlu mengubah metode pribadi ini menjadi pola strategi (dan sekarang perlu lulus strategi yang dipermainkan dalam tes Anda), mungkin Anda salah menempatkan tanggung jawab di sana-sini dan membagi kelas yang ada.

Ketika Anda memperbaiki kode yang ada, TDD tampaknya sangat cocok, tetapi ketika Anda menulis semuanya dari awal, API yang Anda tulis tesnya agak "buram" kecuali jika Anda melakukan desain besar di depan. Apa yang Anda lakukan ketika Anda sudah memiliki 30 tes pada metode yang tandatangannya (dan untuk bagian itu, perilaku) berubah? Itu banyak tes yang harus diubah begitu dijumlahkan.


3
30 tes pada satu metode? Kedengarannya seperti metode yang terlalu rumit, atau Anda menulis terlalu banyak tes.
Minthos

Yah saya mungkin telah sedikit melebih-lebihkan untuk mengungkapkan maksud saya. Setelah memeriksa kode saya biasanya memiliki kurang dari 10 metode per tes dengan sebagian besar dari mereka berada di bawah 5. Tetapi keseluruhan "kembali dan mengubah mereka" bagian telah cukup frustasi.
Vytautas Mackonis

6
@Minthos: Saya dapat memikirkan 6 tes dari atas kepala saya bahwa metode apa pun yang mengambil string akan sering gagal atau berkinerja buruk terhadap penulisan draf pertama (null, kosong, terlalu lama, tidak terlokalisasi dengan baik, penskalaan perf buruk) . Demikian pula untuk metode pengambilan koleksi. Untuk metode non-sepele, 30 terdengar besar, tetapi tidak terlalu tidak realistis.
Steven Evers

Jawaban:


13

Apa yang Anda sebut "desain besar di depan" saya sebut "perencanaan arsitektur kelas Anda yang masuk akal."

Anda tidak dapat menumbuhkan arsitektur dari tes unit. Bahkan Paman Bob mengatakan itu.

Jika Anda tidak memikirkan arsitektur, jika yang Anda lakukan adalah mengabaikan arsitektur dan melakukan tes bersama dan membuatnya lulus, Anda menghancurkan hal yang akan membuat bangunan tetap terjaga karena konsentrasi pada struktur sistem dan keputusan desain yang solid yang membantu sistem mempertahankan integritas strukturalnya.

http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf , Halaman 4

Saya pikir akan lebih masuk akal untuk mendekati TDD dari perspektif memvalidasi desain struktural Anda. Bagaimana Anda tahu desainnya salah jika Anda tidak mengujinya? Dan bagaimana Anda memverifikasi bahwa perubahan Anda sudah benar tanpa juga mengubah tes asli?

Perangkat lunak "lunak" justru karena dapat berubah. Jika Anda merasa tidak nyaman dengan jumlah perubahan, terus mendapatkan pengalaman dalam desain arsitektur, dan jumlah perubahan yang perlu Anda buat agar arsitektur aplikasi Anda akan berkurang seiring waktu.


Masalahnya adalah, bahkan dengan "perencanaan yang masuk akal" Anda berharap banyak untuk berubah. Saya biasanya membiarkan sekitar 80% arsitektur awal saya tetap utuh dengan beberapa perubahan di antaranya. 20% itu mengganggu saya.
Vytautas Mackonis

2
Saya pikir itu sifat pengembangan perangkat lunak. Anda tidak dapat berharap mendapatkan seluruh arsitektur dengan benar pada upaya pertama.
Robert Harvey

2
+1, dan itu tidak bertentangan dengan TDD. TDD dimulai ketika Anda mulai menulis kode, tepatnya saat desain berakhir. TDD dapat membantu Anda melihat apa yang Anda lewatkan dalam desain Anda, memungkinkan Anda untuk memperbaiki desain dan implementasi, dan melanjutkan.
Steven Evers

2
Sebenarnya menurut Bob (dan saya setuju dengannya sepenuh hati) menulis kode adalah desain juga. Memiliki arsitektur tingkat tinggi jelas diperlukan tetapi desain tidak berakhir ketika Anda menulis kode Anda.
Michael Brown

Benar-benar jawaban bagus yang mengenai paku di kepala. Saya melihat begitu banyak orang, baik untuk dan melawan TDD yang tampaknya mengambil "tidak ada desain besar di depan" sebagai "tidak mendesain sama sekali, hanya kode" ketika itu sebenarnya sebuah pemogokan terhadap tahap desain air terjun gila tua. Desain selalu merupakan investasi waktu yang baik dan sangat penting untuk keberhasilan setiap proyek non-sepele.
sara

3

Jika Anda melakukan TDD. Anda tidak dapat mengubah tanda tangan dan perilaku tanpa didorong oleh tes. Jadi, 30 tes yang gagal dihapus dalam proses atau diubah / dire-refraktekan bersama dengan kode. Atau sekarang sudah usang, aman untuk dihapus.

Anda tidak dapat mengabaikan 30 kali merah dalam siklus merah-hijau-refactor Anda?

Tes Anda harus di refactored di samping kode produksi Anda. Jika Anda mampu, jalankan kembali semua tes setelah setiap perubahan.

Jangan takut untuk menghapus tes TDD. Beberapa tes akhirnya menguji blok bangunan untuk mencapai hasil yang diinginkan. Hasil yang diinginkan pada tingkat fungsional adalah yang terpenting. Pengujian di sekitar langkah-langkah perantara dalam algoritma yang Anda pilih / temukan mungkin atau mungkin tidak bernilai banyak ketika ada lebih dari satu cara untuk mencapai hasil atau Anda awalnya menemui jalan buntu.

Terkadang Anda dapat membuat beberapa tes integrasi yang layak, menyimpannya dan menghapus sisanya. Ini agak tergantung apakah Anda bekerja dalam ke luar atau dari atas ke bawah dan seberapa besar langkah yang Anda ambil.


1

Seperti yang dikatakan Robert Harvey, Anda mungkin mencoba menggunakan TDD untuk sesuatu yang harus ditangani oleh alat konseptual yang berbeda (yaitu: "desain" atau "pemodelan").

Cobalah untuk merancang (atau "model") sistem Anda dengan cara yang cukup abstrak ("umum", "tidak jelas"). Misalnya, jika Anda harus memodelkan mobil, cukup kelas mobil dengan beberapa metode dan bidang yang tidak jelas, seperti startEngine () dan kursi int. Yaitu: jelaskan apa yang ingin Anda paparkan kepada publik , bukan bagaimana Anda ingin mengimplementasikannya. Cobalah untuk mengekspos fungsionalitas dasar saja (baca, tulis, mulai, hentikan, dll) dan biarkan kode klien menguraikannya (persiapanMyScene (), killTheEnemy (), dll).

Tulis tes Anda dengan asumsi antarmuka publik sederhana ini.

Ubah perilaku internal kelas dan metode Anda kapan pun Anda membutuhkannya.

Jika dan ketika Anda perlu mengubah antarmuka publik dan ruang uji Anda, berhentilah dan pikirkan. Kemungkinan besar ini adalah tanda bahwa ada sesuatu yang salah di API Anda dan dalam desain / pemodelan Anda.

Sudah biasa mengubah API. Sebagian besar sistem pada versi 1.0 mereka secara eksplisit memperingatkan pemrogram / pengguna terhadap kemungkinan perubahan dalam API mereka. Meskipun demikian, aliran perubahan API yang terus-menerus dan tidak terkontrol merupakan tanda jelas dari desain yang buruk (atau sama sekali tidak ada).

BTW: Anda biasanya harus hanya memiliki beberapa tes per metode. Metode, menurut definisi, harus mengimplementasikan "tindakan" yang didefinisikan dengan jelas pada beberapa jenis data. Di dunia yang sempurna, ini harus menjadi tindakan tunggal yang sesuai dengan satu tes. Di dunia nyata bukan hal yang aneh (dan tidak salah) memiliki beberapa "versi" yang berbeda dari aksi yang sama dan beberapa tes yang sesuai. Yang pasti, Anda harus menghindari memiliki 30 tes pada metode yang sama. Ini adalah tanda yang jelas bahwa metode ini mencoba melakukan terlalu banyak (dan kode internalnya tumbuh di luar kendali).


0

Saya melihatnya dari perspektif pengguna. Misalnya, jika API Anda mengizinkan saya membuat objek Orang dengan nama dan usia, lebih baik ada konstruktor Orang (nama string, usia int) dan metode pengaksesor untuk nama dan usia. Sangat mudah untuk membuat test case untuk Orang baru dengan dan tanpa nama dan umur.

doug

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.