Saya tidak mengerti bagaimana TDD membantu saya mendapatkan desain yang baik jika saya membutuhkan desain untuk mulai mengujinya

50

Saya mencoba untuk membungkus kepala saya di sekitar TDD, khususnya bagian pengembangan. Saya telah melihat beberapa buku, tetapi yang saya temukan terutama menangani bagian pengujian - Sejarah NUnit, mengapa pengujian itu baik, Merah / Hijau / Refactor dan cara membuat String Calculator.

Bagus, tapi itu "hanya" Unit Testing, bukan TDD. Secara khusus, saya tidak mengerti bagaimana TDD membantu saya mendapatkan desain yang baik jika saya membutuhkan Desain untuk mulai mengujinya.

Untuk menggambarkan, bayangkan 3 persyaratan ini:

  • Katalog perlu memiliki daftar produk
  • Katalog harus mengingat produk mana yang dilihat pengguna
  • Pengguna harus dapat mencari produk

Pada titik ini, banyak buku menarik kelinci ajaib keluar dari topi dan hanya menyelami "Menguji Layanan Produk", tetapi mereka tidak menjelaskan bagaimana mereka sampai pada kesimpulan bahwa ada Layanan Produk di tempat pertama. Itulah bagian "Pengembangan" dalam TDD yang saya coba pahami.

Harus ada desain yang sudah ada, tetapi hal-hal di luar entitas-layanan (yaitu: Ada Produk, jadi harus ada ProductService) tidak ditemukan di mana pun (misalnya, persyaratan kedua mengharuskan saya memiliki beberapa konsep tentang Pengguna, tetapi di mana saya ingin mengingatkan fungsi ini? Dan apakah Cari fitur dari ProductService atau SearchService yang terpisah? Bagaimana saya tahu mana yang harus saya pilih?)

Menurut SOLID , saya akan membutuhkan UserService, tetapi jika saya mendesain sistem tanpa TDD, saya mungkin berakhir dengan sejumlah Layanan Metode-Tunggal. Bukankah TDD dimaksudkan untuk membuat saya menemukan desain saya di tempat pertama?

Saya adalah pengembang .net, tetapi sumber daya Java juga akan berfungsi. Saya merasa sepertinya tidak ada contoh aplikasi atau buku nyata yang berhubungan dengan lini aplikasi bisnis nyata. Bisakah seseorang memberikan contoh yang jelas yang menggambarkan proses pembuatan desain menggunakan TDD?

Michael Stum
sumber
2
TDD hanyalah bagian dari keseluruhan metodologi pengembangan. Tentu saja Anda perlu menggunakan beberapa jenis desain (baik di muka, atau yang lebih baik dari evolusi) untuk menyatukan semuanya.
Euforia
3
@gnat: Ini pertanyaan mengapa buku TDD tidak membuat proses desain lebih jelas.
Robert Harvey
4
@gnat: Itu hasil edit Anda, bukan milik saya. :) Lihat perubahan saya pada judul pertanyaan dan isi.
Robert Harvey
9
Jika Anda telah membaca karya Robert C. Martin atau mungkin menonton salah satu videonya, Anda akan melihat bahwa ia sering memiliki desain di benaknya tetapi ia belum menikah dengannya. Dia percaya bahwa konsep desain yang tepat akan muncul dari tesnya, tetapi dia tidak memaksanya. Dan pada akhirnya, terkadang desain itu berhasil, dan terkadang tidak. Maksud saya di sini adalah bahwa pengalaman Anda sebelumnya akan memandu Anda, tetapi tes harus mengarahkan Anda. Tes harus dapat mengembangkan atau menyanggah desain Anda.
Anthony Pegram
3
Jadi ini bukan soal pengujian, ini soal desain. Hanya saja itu tidak benar-benar membantu Anda dengan desain, sama seperti membantu Anda memvalidasi desain. Tapi bukankah itu! @ # $ Pengujian?
Erik Reppen

Jawaban:

17

Ide TDD adalah memulai dengan pengujian dan bekerja dari situ. Dengan demikian, untuk mengambil contoh Anda "Katalog perlu memiliki daftar produk" dapat dilihat sebagai memiliki tes "Periksa produk dalam katalog" dan dengan demikian ini adalah tes pertama. Sekarang, apa yang menyimpan katalog? Apa yang menahan suatu produk? Itu adalah bagian berikutnya dan idenya adalah untuk mendapatkan beberapa bagian yang akan menjadi sesuatu seperti ProductService yang akan lahir dari mendapatkan tes pertama yang harus lulus.

Ide TDD adalah untuk memulai dengan tes dan kemudian menulis kode yang membuat tes lulus sebagai poin pertama. Unit test adalah bagian dari ini ya, tetapi Anda tidak melihat gambaran keseluruhan yang dibentuk dengan memulai dengan tes dan kemudian menulis kode sehingga tidak ada titik buta pada saat ini karena belum ada kode apa pun.


Tutorial Pengembangan Tes Didorong di mana slide 20-22 adalah yang utama. Idenya adalah untuk mengetahui fungsi apa yang harus dilakukan sebagai hasilnya, menulis tes untuk itu dan kemudian membangun solusi. Bagian desain akan bervariasi tergantung pada apa yang diperlukan itu mungkin atau mungkin tidak mudah dilakukan. Poin kuncinya adalah menggunakan TDD dari awal daripada mencoba memperkenalkan terlambat ke dalam proyek. Jika Anda memulai dengan tes terlebih dahulu, ini dapat membantu dan kemungkinan perlu diperhatikan. Jika Anda mencoba menambahkan tes nanti, itu menjadi sesuatu yang dapat ditunda atau ditunda. Slide-slide selanjutnya juga dapat bermanfaat.


Manfaat utama TDD adalah bahwa dengan memulai dengan tes, Anda tidak terkunci ke dalam desain pada awalnya. Dengan demikian, idenya adalah untuk membangun tes dan membuat kode yang akan lulus tes tersebut sebagai metodologi pengembangan. Sebuah Desain Besar di Depan dapat menyebabkan masalah karena ini memberikan ide untuk mengunci sesuatu pada tempatnya yang membuat sistem yang dibangun menjadi kurang gesit pada akhirnya.


Robert Harvey menambahkan ini dalam komentar yang layak untuk disebutkan dalam jawabannya:

Sayangnya saya pikir ini adalah kesalahpahaman umum tentang TDD: Anda tidak dapat menumbuhkan arsitektur perangkat lunak dengan hanya menulis tes unit dan membuatnya lulus. Tes unit menulis memang memengaruhi desain, tetapi tidak membuat desain. Anda harus melakukannya.

JB King
sumber
31
@MichaelStum: Sayangnya saya pikir ini adalah kesalahpahaman umum tentang TDD: Anda tidak dapat menumbuhkan arsitektur perangkat lunak hanya dengan menulis unit test dan membuatnya lulus. Tes unit menulis memang memengaruhi desain, tetapi tidak membuat desain. Anda harus melakukannya.
Robert Harvey
4
@RobertHarvey, JimmyHoffa: jika saya dapat memilih komentar Anda sebanyak 100 kali, saya akan melakukannya!
Doc Brown
9
@Robert Harvey: Saya senang Anda menulis tentang kesalahpahaman umum ini: Saya terlalu sering mendengar bahwa seseorang hanya perlu duduk dan menulis semua jenis unit test dan desain hanya akan "muncul" secara spontan. Dan jika desain Anda buruk, itu karena Anda tidak menulis tes unit yang cukup. Saya setuju dengan Anda bahwa tes adalah alat untuk menentukan dan memverifikasi persyaratan untuk desain Anda, tetapi "Anda harus melakukan" desain sendiri. Saya sangat setuju.
Giorgio
4
@Giorgo, RobertHarvey: +1000 ke RobertHarvey dari saya juga. Sayangnya, kesalahpahaman itu cukup umum sehingga beberapa praktisi "ahli" TDD / Agile percaya itu benar. Seperti misalnya, mereka berpura-pura Anda dapat "mengembangkan" pemecah sudoku dari TDD, tanpa pengetahuan domain atau analisis apa pun . Saya ingin tahu apakah Ron Jeffries pernah menerbitkan tindak lanjut tentang batasan TDD atau menjelaskan mengapa dia tiba-tiba menghentikan eksperimennya tanpa kesimpulan atau pembelajaran apa pun.
Andres F.
3
@Andres F: Saya tahu cerita tentang sudoku, dan saya pikir ini sangat menarik. Saya pikir beberapa pengembang membuat kesalahan dengan berpikir bahwa suatu alat (misalnya TDD atau SCRUM) dapat menggantikan pengetahuan domain dan upaya mereka sendiri, dan berharap bahwa dengan secara mekanis menerapkan metode tertentu, perangkat lunak yang baik akan secara ajaib "muncul". Mereka sering orang yang tidak suka menghabiskan terlalu banyak waktu dalam analisis dan desain dan lebih suka untuk membuat kode sesuatu secara langsung. Bagi mereka, mengikuti metodologi tertentu adalah alibi untuk tidak melakukan desain yang tepat. Tapi ini IMHO penyalahgunaan TDD.
Giorgio
8

Untuk apa nilainya, TDD membantu saya mencapai desain terbaik lebih cepat daripada tidak melakukan TDD. Saya mungkin akan datang ke desain terbaik dengan atau tanpa itu. Tetapi waktu itu saya akan menghabiskan memikirkannya dan mengambil beberapa tusukan pada kode dihabiskan menulis tes sebagai gantinya. Dan ini lebih sedikit waktu. Untuk saya. Tidak untuk semua orang. Dan, bahkan jika itu mengambil jumlah waktu yang sama, itu akan meninggalkan saya dengan serangkaian tes, sehingga refactoring akan lebih aman, mengarah ke kode yang lebih baik di telepon.

Bagaimana cara melakukannya?

Pertama, ini mendorong saya untuk memikirkan setiap kelas sebagai layanan untuk beberapa kode klien. Kode yang lebih baik berasal dari memikirkan tentang bagaimana kode panggilan ingin menggunakan API daripada khawatir tentang bagaimana kode itu sendiri akan terlihat.

Kedua, itu menghentikan saya menulis terlalu banyak kompleksitas siklometik ke dalam satu metode, sementara saya memikirkannya. Setiap jalur tambahan melalui metode akan cenderung menggandakan jumlah tes yang perlu saya lakukan. Kemalasan semata-mata menyatakan bahwa setelah saya menambahkan terlalu banyak logika, dan saya harus menulis 16 tes untuk menambahkan satu syarat, sekarang saatnya untuk menarik sebagian ke dalam metode / kelas lain dan mengujinya secara terpisah.

Sangat sederhana. Itu bukan alat desain ajaib.

pdr
sumber
6

Saya mencoba membungkus kepala saya di sekitar TDD ... Sebagai ilustrasi, bayangkan 3 persyaratan ini:

  • Katalog perlu memiliki daftar produk
  • Katalog harus mengingat produk mana yang dilihat pengguna

Persyaratan ini harus dinyatakan kembali dalam istilah manusia. Siapa yang ingin tahu produk mana yang dilihat pengguna sebelumnya? Pengguna? Seorang tenaga penjualan?

  • Pengguna harus dapat mencari produk

Bagaimana? Dengan nama? Dengan merek? Langkah pertama dalam pengembangan yang digerakkan oleh tes adalah menentukan tes, misalnya:

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

Pada titik ini, banyak buku menarik kelinci ajaib keluar dari topi dan hanya menyelami "Menguji Layanan Produk", tetapi mereka tidak menjelaskan bagaimana mereka sampai pada kesimpulan bahwa ada Layanan Produk di tempat pertama.

Jika ini adalah satu-satunya persyaratan, saya pasti tidak akan melompat untuk membuat ProductService. Saya mungkin membuat halaman web yang sangat sederhana dengan daftar produk statis. Itu akan bekerja dengan sempurna sampai Anda mendapatkan persyaratan untuk menambah dan menghapus produk. Pada titik itu saya mungkin memutuskan paling sederhana untuk menggunakan database relasional dan ORM, dan membuat kelas Produk yang dipetakan ke satu tabel. Masih tidak ada ProductService. Kelas-kelas seperti ProductService akan dibuat kapan dan jika dibutuhkan. Mungkin ada beberapa permintaan web yang perlu melakukan kueri atau pembaruan yang sama. Kemudian kelas ProductService akan dibuat untuk mencegah duplikasi kode.

Singkatnya, TDD mendorong kode untuk ditulis. Desain terjadi saat Anda membuat pilihan implementasi, dan kemudian mengubah kode menjadi kelas untuk menghilangkan duplikasi dan mengontrol dependensi. Saat Anda menambahkan kode, Anda harus membuat kelas baru untuk menjaga kode SOLID. Tetapi Anda tidak perlu memutuskan sebelumnya bahwa Anda akan membutuhkan kelas Produk dan kelas ProductService. Anda mungkin menemukan bahwa hidup ini baik-baik saja hanya dengan kelas Produk.

kevin cline
sumber
OK, tidak ProductService. Tetapi bagaimana TDD memberi tahu Anda bahwa Anda membutuhkan database dan ORM?
Robert Harvey
4
@ Robert: tidak. Ini keputusan desain, berdasarkan penilaian saya tentang cara paling efektif untuk memenuhi persyaratan. Tapi keputusannya bisa berubah.
kevin cline
1
Desain yang baik tidak akan pernah diproduksi sebagai efek samping dari beberapa proses sewenang-wenang. Memiliki sistem atau model untuk bekerja dengan dan membingkai hal-hal hebat, tetapi tes-pertama-TDD, IMO, menyentuh konflik kepentingan dengan juga menjual dirinya sebagai sesuatu yang akan menjamin orang tidak akan digigit tiba-tiba oleh efek samping dari buruk kode yang seharusnya tidak terjadi di tempat pertama. Desain membutuhkan refleksi, kesadaran, dan pemikiran ke depan. Anda tidak belajar itu dari memangkas gejala yang ditemukan secara otomatis dari pohon. Anda mempelajarinya dengan mencari tahu bagaimana menghindari cabang mutan jahat di tempat pertama.
Erik Reppen
Saya pikir tesnya 'tambah satu produk; reboot komputer dan restart sistem; produk yang ditambahkan harus tetap terlihat. ' menunjukkan dari mana kebutuhan untuk beberapa jenis database berasal (tapi itu masih berupa file datar atau XML).
yatima2975
3

Orang lain mungkin tidak setuju, tetapi bagi saya banyak metodologi baru bergantung pada asumsi bahwa pengembang akan melakukan sebagian besar dari apa yang metodologi lama katakan hanya karena kebiasaan atau kebanggaan pribadi, bahwa pengembang biasanya melakukan sesuatu yang cukup jelas. bagi mereka, dan pekerjaan tersebut dikemas dalam bahasa yang bersih atau bagian yang lebih bersih dari bahasa yang agak berantakan sehingga Anda dapat melakukan semua bisnis pengujian.

Beberapa contoh di mana saya mengalami ini di masa lalu:

  • Ambil sekelompok kontraktor pekerjaan khusus dan beri tahu mereka bahwa tim mereka Agile and Test First. Mereka sering tidak memiliki kebiasaan selain untuk bekerja secara spesifik dan mereka tidak peduli dengan kualitas pekerjaan selama itu berlangsung cukup lama untuk menyelesaikan proyek.

  • Cobalah dan lakukan sesuatu tes baru terlebih dahulu, habiskan sebagian besar waktu Anda untuk melakukan tes ripping ketika Anda menemukan berbagai pendekatan dan antarmuka adalah omong kosong.

  • Kode sesuatu tingkat rendah dan baik ditampar karena kurangnya cakupan, atau menulis banyak tes yang tidak berarti banyak karena Anda tidak dapat mengejek perilaku mendasar Anda terikat.

  • Setiap situasi di mana Anda tidak memiliki cukup banyak mekanisme yang mendasari di tempat sebelumnya untuk menambahkan fitur yang dapat diuji tanpa menulis sekelompok bit mendasar yang tidak dapat diuji terlebih dahulu, seperti subsistem disk, atau antarmuka komunikasi tingkat tcpip.

Jika Anda melakukan TDD dan itu berfungsi untuk Anda, bagus untuk Anda, tetapi ada banyak hal (seluruh pekerjaan, atau tahapan proyek) di luar sana di mana ini hanya tidak menambah nilai.

Contoh Anda terdengar seperti Anda bahkan belum ke desain, jadi Anda perlu memiliki percakapan arsitektur, atau Anda membuat prototipe. Anda harus melewati beberapa dari itu terlebih dahulu menurut saya.

Tagihan
sumber
1

Saya yakin bahwa TDD adalah pendekatan yang sangat berharga untuk desain rinci sistem - yaitu API dan model objek. Namun, untuk sampai ke titik dalam proyek di mana Anda akan mulai menggunakan TDD, Anda harus memiliki gambaran besar dari desain yang sudah dimodelkan dalam beberapa mode dan Anda perlu memiliki gambaran besar dari arsitektur yang sudah dimodelkan dalam beberapa mode. @ user414076 parafrase Robert Martin memiliki ide desain dalam pikiran, tetapi tidak menikah dengannya. Persis. Kesimpulan - TDD bukan satu-satunya kegiatan desain yang sedang berlangsung, ini adalah bagaimana detail dari desain disempurnakan. TDD harus didahului oleh kegiatan desain lain dan masuk ke dalam pendekatan keseluruhan (seperti Agile) yang membahas bagaimana desain keseluruhan akan dibuat dan berevolusi.

FYI - dua buku yang saya rekomendasikan pada topik yang memberikan contoh nyata dan realistis:

Tumbuh Perangkat Lunak Berorientasi Objek, Dipandu oleh Tes - menjelaskan dan memberikan contoh proyek penuh. Ini adalah buku tentang desain, bukan pengujian . Pengujian digunakan sebagai sarana untuk menentukan perilaku yang diharapkan selama kegiatan desain.

pengembangan berbasis tes Panduan Praktis - berjalan lambat dan langkah demi langkah melalui pengembangan aplikasi yang lengkap, walaupun kecil.

Chuck Krutsinger
sumber
0

TTD mendorong penemuan desain dengan kegagalan pengujian, bukan kesuksesan, oleh karena itu Anda dapat menguji yang tidak diketahui dan mengulangi secara berulang karena tidak diketahui yang akhirnya mengarah pada rangkaian lengkap unit test - hal yang sangat bagus untuk pemeliharaan berkelanjutan dan hal yang sangat sulit untuk dicoba. retrofit setelah kode ditulis / dirilis.

Sebagai contoh, suatu persyaratan mungkin bahwa input dapat dalam beberapa format berbeda, belum semuanya diketahui. Dengan menggunakan TDD, Anda harus terlebih dahulu menulis tes yang memverifikasi output yang sesuai diberikan dengan format input apa pun . Jelas tes ini akan gagal, jadi Anda menulis kode untuk menangani format yang dikenal dan tes ulang. Karena format yang tidak diketahui diekspos melalui pengumpulan persyaratan, tes baru ditulis sebelum kode ditulis, ini juga harus gagal. Kemudian kode baru ditulis untuk mendukung format baru dan semua tes dijalankan kembali mengurangi peluang untuk regresi.

Juga bermanfaat untuk menganggap unit failure sebagai kode "belum selesai" alih-alih kode "rusak". TDD memungkinkan untuk unit yang belum selesai (kegagalan yang diharapkan), tetapi mengurangi terjadinya unit yang rusak (kegagalan yang tidak terduga).

Daniel Pereira
sumber
1
Saya setuju bahwa ini adalah alur kerja yang valid, tetapi itu tidak benar-benar menjelaskan bagaimana arsitektur tingkat tinggi dapat muncul dari alur kerja seperti itu.
Robert Harvey
1
Benar, arsitektur tingkat tinggi seperti pola MVC, tidak akan muncul dari TDD saja. Tapi, apa yang bisa muncul dari TDD adalah kode yang dirancang agar mudah diuji, yang merupakan pertimbangan desain itu sendiri.
Daniel Pereira
0

Dalam pertanyaan itu dinyatakan:

... banyak buku menarik kelinci ajaib keluar dari topi dan hanya menyelami "Menguji Layanan Produk", tetapi mereka tidak menjelaskan bagaimana mereka sampai pada kesimpulan bahwa ada Layanan Produk di tempat pertama.

Mereka sampai pada kesimpulan itu dengan memikirkan bagaimana mereka akan menguji produk ini. "Produk apa yang melakukan ini?" "Yah, kita bisa membuat layanan". "Oke, mari kita menulis tes untuk layanan seperti itu"

Bryan Oakley
sumber
0

Fungsionalitas dapat memiliki banyak desain dan TDD tidak akan sepenuhnya memberi tahu Anda mana yang terbaik. Bahkan, jika pengujian akan membantu Anda membangun lebih banyak kode modular, ini juga dapat mengarahkan Anda untuk membuat modul yang sesuai dengan persyaratan pengujian dan bukan realitas produksi. Jadi, Anda harus memahami ke mana Anda akan pergi dan bagaimana segala sesuatunya harus sesuai dengan keseluruhan gambar. Jika tidak, ada persyaratan Fungsional dan Non-Fungsional, jangan lupa yang terakhir.

Mengenai desain, saya merujuk pada buku Robert C. Martin (Agile Development) tetapi juga Martin Fowler's Patterns of Arsitektur Aplikasi Perusahaan dan Desain Driver Domain. Yang belakangan terutama sangat sistematis dalam mengekstraksi Entitas dan Hubungan dari persyaratan.

Kemudian, ketika Anda mendapatkan perasaan yang baik tentang opsi yang tersedia untuk Anda tentang cara mengelola entitas tersebut, Anda bisa memberi Anda pendekatan TDD.

Ludovic
sumber
0

Bukankah TDD dimaksudkan untuk membuat saya menemukan desain saya di tempat pertama?

Tidak.

Bagaimana Anda bisa menguji sesuatu yang belum Anda rancang terlebih dahulu?

Untuk menggambarkan, bayangkan 3 persyaratan ini:

  • Katalog perlu memiliki daftar produk
  • Katalog harus mengingat produk mana yang dilihat pengguna
  • Pengguna harus dapat mencari produk

Ini bukan persyaratan, ini adalah definisi data. Saya tidak tahu apa urusan perangkat lunak Anda, tetapi tidak mungkin analis berbicara seperti itu.

Anda perlu tahu apa saja invarian dari sistem Anda.

Suatu persyaratan akan menjadi seperti:

  • Pelanggan dapat memesan produk jumlah tertentu, jika stok produk ini mencukupi.

Jadi jika ini adalah satu-satunya persyaratan, Anda mungkin memiliki kelas seperti:

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

Kemudian menggunakan TDD, Anda akan menulis test case sebelum menerapkan metode order ().

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

Jadi tes kedua akan gagal, maka Anda dapat menerapkan metode order () sesuka Anda.

Guillaume
sumber
0

Anda cukup benar TDD akan menghasilkan implementasi yang baik dari desain yang diberikan. Ini tidak akan membantu proses desain Anda.

James Anderson
sumber
Namun itu memberi Anda jaring pengaman untuk meningkatkan desain tanpa melanggar kode kerja. Ini adalah refactoring yang dilewati kebanyakan orang.
Adrian Schneider
-3

TDD banyak membantu, namun ada bagian penting dalam pengembangan perangkat lunak. Pengembang harus mendengarkan kode yang sedang ditulis. Refactoring adalah bagian ke-3 dalam siklus TDD. Ini adalah langkah utama di mana pengembang harus fokus dan berpikir sebelum pergi ke tes merah berikutnya. Apakah ada duplikasi? Apakah prinsip SOLID diterapkan? Bagaimana dengan kohesi tinggi dan kopling rendah? Bagaimana dengan nama? Lihatlah lebih dekat pada kode yang muncul dari tes dan lihat apakah ada sesuatu yang perlu diubah, didesain ulang. Mempertanyakan kode dan kode akan memberi tahu Anda, bagaimana ia ingin dirancang. Saya biasanya menulis beberapa tes, memeriksa daftar itu dan membuat desain sederhana pertama, tidak perlu "final", biasanya tidak, karena itu berubah ketika menambahkan tes baru. Di situlah desain datang.

Adronius
sumber