TDD: Apakah saya melakukannya dengan benar?

14

Saya seorang programmer baru (baru belajar selama sekitar satu tahun) dan dalam tujuan saya untuk menjadi lebih baik, saya baru saja belajar tentang TDD. Saya ingin membiasakan menggunakannya karena tampaknya sangat membantu. Saya ingin memeriksa dan memastikan saya menggunakannya dengan benar.

Apa yang aku lakukan:

  1. Pikirkan metode baru yang saya butuhkan.
  2. Buat tes untuk metode itu.
  3. Tes gagal.
  4. Menulis metode
  5. Lulus tes.
  6. Metode refactor.
  7. Ulang.

Saya melakukan ini untuk SETIAP metode yang saya tulis, adakah yang harus saya hindari? Kemudian saya biasanya memikirkan cara untuk menguji metode saya yang sudah ada dengan cara atau situasi yang berbeda. Haruskah saya membuat tes-tes baru yang saya pikirkan, atau karena setiap metode sudah memiliki tes sendiri apakah saya tidak perlu repot? Dapatkah saya LEBIH menguji kode saya, saya kira adalah perhatian utama saya dalam menanyakan ini.

EDIT

Juga, ini adalah sesuatu yang saya hanya ingin tahu. Ketika melakukan sesuatu seperti membuat GUI, apakah TDD diperlukan dalam situasi itu? Secara pribadi, saya tidak bisa memikirkan bagaimana saya akan menulis tes untuk itu.

cgasser
sumber
5
Anda sudah melakukannya jauh lebih baik daripada profesional berpengalaman yang mengatakan mereka sedang menguji segalanya (tetapi tidak).
yannis
Apa yang Anda gambarkan bukanlah Roh TDD.
1
Anda mungkin ingin melihat ATDD atau BDD.
dietbuddha
Mungkin mulai lebih tinggi - pikirkan modul baru yang Anda butuhkan.

Jawaban:

16

Apa yang Anda gambarkan sebagai alur kerja tidak menurut saya Roh TDD.

Sinopsis buku Kent Becks di Amazon mengatakan:

Sederhananya, pengembangan berbasis tes dimaksudkan untuk menghilangkan rasa takut dalam pengembangan aplikasi.Sementara beberapa rasa takut itu sehat (sering dipandang sebagai hati nurani yang memberitahu programmer untuk "berhati-hati!"), Penulis percaya bahwa produk sampingan dari rasa takut termasuk programmer sementara, pemarah, dan tidak komunikatif yang tidak mampu menyerap kritik yang membangun. Ketika tim pemrograman membeli TDD, mereka langsung melihat hasil positif. Mereka menghilangkan rasa takut yang terlibat dalam pekerjaan mereka, dan lebih siap untuk mengatasi tantangan sulit yang dihadapi mereka. TDD menghilangkan sifat-sifat sementara, itu mengajarkan programmer untuk berkomunikasi, dan itu mendorong anggota tim untuk mencari kritik. Namun, bahkan penulis mengakui bahwa kekasaran harus dikerjakan secara individual! Singkatnya, premis di balik TDD adalah bahwa kode harus terus diuji dan di-refactored.

TDD praktis

Pengujian otomatis formal, terutama Pengujian Unit, setiap metode di setiap kelas sama buruknya dengan anti-pola dan tidak menguji apa pun. Ada keseimbangan yang bisa didapat. Apakah Anda menulis tes unit untuk setiap setXXX/getXXXmetode, mereka juga metode!

Tes juga dapat membantu menghemat waktu dan uang, tetapi jangan lupa bahwa mereka membutuhkan waktu dan uang untuk dikembangkan dan mereka adalah kode, sehingga biayanya memerlukan waktu dan uang untuk mempertahankannya. Jika mereka berhenti karena kurangnya perawatan maka mereka menjadi tanggung jawab lebih dari manfaat.

Seperti segala sesuatu seperti ini, ada keseimbangan yang tidak dapat didefinisikan oleh siapa pun kecuali diri Anda sendiri. Dogma apa pun itu mungkin lebih salah dan benar.

Metrik yang baik adalah kode yang penting untuk logika bisnis dan harus sering diubah berdasarkan perubahan persyaratan. Hal-hal itu memerlukan tes formal yang otomatis, yang akan menjadi pengembalian investasi yang besar.

Anda akan sangat kesulitan menemukan banyak toko profesional yang berfungsi dengan baik. Tidak masuk akal secara bisnis untuk mengeluarkan uang untuk menguji hal-hal yang untuk semua tujuan praktis tidak akan pernah berubah setelah tes asap sederhana dilakukan. Menulis tes unit otomatis formal untuk .getXXX/.setXXXmetode adalah contoh utama dari ini, buang-buang waktu.

Sekarang sudah dua dekade sejak ditunjukkan bahwa pengujian program dapat secara meyakinkan menunjukkan keberadaan bug, tetapi tidak pernah dapat menunjukkan ketidakhadiran mereka. Setelah mengutip pernyataan yang dipublikasikan dengan baik ini, insinyur perangkat lunak kembali ke urutan hari dan terus memperbaiki strategi pengujiannya, seperti alkemis dahulu kala, yang terus menyempurnakan pemurnian chrysocosmic-nya.

- Edsger W. Djikstra . (Ditulis pada tahun 1988, jadi sekarang mendekati 4,5 dekade.)

Lihat juga jawaban ini .

Komunitas
sumber
1
Itu cukup banyak membahas apa yang saya khawatirkan. Saya merasa bahwa saya seharusnya tidak menguji setiap metode seperti saya, tetapi tidak yakin. Sepertinya saya mungkin masih perlu membaca lebih banyak tentang TDD.
cgasser
@ kevincline Sebagian besar waktu setXXX/getXXXtidak diperlukan sama sekali :)
Chip
1
Ketika Anda memoise getXXX yang sepele dan salah, atau memperkenalkan pemuatan malas di getXXX Anda dan salah, maka Anda akan tahu bahwa kadang-kadang Anda benar-benar ingin menguji getter Anda.
Frank Shearar
13

Anda sangat dekat. Coba pikirkan dengan cara yang sedikit berbeda ini.

  1. Pikirkan perilaku baru yang saya butuhkan.
  2. Buat tes untuk perilaku itu.
  3. Tes gagal.
  4. Tulis baru atau perluas metode yang ada.
  5. Lulus tes.
  6. Kode refactor.
  7. Ulang.

Jangan otomatis membuat getter dan setter untuk setiap properti . Jangan memikirkan seluruh metode dan menulis tes untuk mencakup semua fungsionalitas . Cobalah untuk merangkum properti di dalam kelas dan tulis metode untuk memberikan perilaku yang Anda butuhkan. Biarkan metode Anda berkembang menjadi desain yang baik alih-alih mencoba merencanakannya di muka. Ingatlah bahwa TDD adalah proses desain, bukan proses pengujian. Keuntungan yang dimilikinya dibandingkan proses desain lainnya adalah meninggalkan aliran tes regresi otomatis, daripada selembar kertas yang Anda buang ke tempat sampah.

Juga, ingat tiga aturan TDD Paman Bob .

  1. Anda tidak diperbolehkan untuk menulis kode produksi apa pun kecuali untuk membuat lulus uji unit yang gagal.
  2. Anda tidak diperbolehkan menulis lebih dari satu unit tes daripada yang cukup untuk gagal; dan kegagalan kompilasi adalah kegagalan.
  3. Anda tidak diperbolehkan menulis kode produksi lebih dari cukup untuk lulus satu unit uji gagal.
pdr
sumber
1
@Zexanima: Anda melakukan jauh lebih baik daripada kebanyakan dari kita setelah satu tahun. Hanya mencoba mengarahkan Anda ke langkah selanjutnya.
pdr
2
Saya pikir 3 aturan yang Anda tautkan; kedengarannya idilis, sangat dogmatis dan sangat tidak realistis di 99% dari semua toko produksi yang akan ditemui siapa pun.
1
@ FrankShearar atau dapat dilihat sebagai omong kosong yang tidak praktis dari seorang ekstremis fundamentalis dan grosir yang diabaikan. Saya telah bekerja di toko-toko yang memiliki sikap dogmatis ini, mereka menerima dogma secara harfiah dan melewatkan intinya; menulis tes yang tidak menguji salah satu kode aktual mereka dengan cara yang praktis dan akhirnya hanya menguji kerangka kerja Mocking and Dependency Injection untuk membingungkan apa yang paling penting.
1
@ pdr. Roh sesuatu secara diametris bertentangan dengan kanonisasi resmi dogmatis dari hal itu. Adalah satu hal untuk memiliki filosofi dan yang lain untuk mengubahnya menjadi agama . TDD lebih sering berbicara tentang agama dogmatis hitam dan putih . Tiga aturan itu kedengaran dogmatis dan religius dalam presentasi dan yang didengar adalah Tes, Tes, Tes mantra, bagi seseorang seperti OP, mereka menganggapnya harfiah dan yang menyebabkan lebih banyak ruginya daripada kebaikan. Saya balas Frank bahwa pernyataan polarisasi bisa lebih membahayakan tujuannya daripada kebaikan.
2
Maksud saya adalah bahwa dogmatisme datang dari menerima secara buta sesuatu sebagai Injil . Ambil pernyataan polarisasi, cobalah, buat itu memaksa Anda keluar dari zona nyaman Anda. Anda tidak dapat mengevaluasi pengorbanan yang terlibat dalam TDD jika Anda tidak mencoba pendekatan ekstrim 3-point-all-or-nothing, karena Anda tidak akan memiliki data .
Frank Shearar
5

Beberapa hal untuk ditambahkan ke tanggapan orang lain:

  1. Ada yang namanya over testing. Anda ingin memastikan tes unit Anda tumpang tindih sesedikit mungkin. Tidak ada gunanya memiliki beberapa tes memverifikasi kondisi yang sama di bagian kode yang sama. Di sisi lain, ketika Anda memperbaiki kode produksi Anda dan Anda memiliki banyak tes yang tumpang tindih dengan bagian itu, Anda harus kembali dan memperbaiki semua tes itu. Sedangkan jika mereka tidak tumpang tindih, maka satu perubahan paling banyak hanya akan merusak satu tes.

  2. Hanya karena Anda memikirkan cara yang lebih baik untuk menulis tes, saya tidak akan kembali ke sana dan mulai menulis ulang. Ini akan kembali ke individu-individu yang terus menulis dan menulis ulang kelas / fungsi yang sama karena mereka berusaha membuatnya sempurna. Itu tidak akan pernah sempurna, jadi teruskan. Ketika Anda menemukan metode yang lebih baik, simpan di belakang pikiran Anda (atau tambahkan komentar tes). Lain kali Anda berada di sana, dan Anda melihat manfaat langsung beralih ke cara baru, itulah saatnya untuk refactor. Jika tidak, jika fitur selesai dan Anda pindah dan semuanya berfungsi, biarkan berfungsi.

  3. TDD berfokus pada memberikan nilai langsung, tidak hanya memastikan setiap fungsi dapat diuji. Ketika Anda menambahkan fungsionalitas, mulailah dengan bertanya "apa yang dibutuhkan klien". Kemudian tentukan antarmuka untuk memberikan klien apa yang dibutuhkan. Kemudian laksanakan apa pun untuk lulus ujian. TDD hampir seperti pengujian skenario penggunaan kasus (termasuk semua "bagaimana-jika"), daripada hanya mengkodekan fungsi publik dan menguji masing-masing.

  4. Anda bertanya tentang pengujian kode GUI. Cari pola "Humble Dialog" dan "MVVM". Gagasan di balik kedua hal ini adalah Anda membuat satu set kelas "model tampilan", yang sebenarnya tidak memiliki logika khusus UI. Namun, kelas-kelas ini akan memiliki semua logika bisnis yang biasanya merupakan bagian dari UI Anda dan kelas-kelas ini harus 100% diuji. Yang tersisa adalah shell UI yang sangat tipis dan ya, biasanya shell tersebut dibiarkan tanpa cakupan pengujian, tetapi pada saat itu seharusnya hampir tidak ada logika.

  5. Jika Anda memiliki sebagian besar kode yang ada, seperti yang disarankan beberapa orang lainnya, Anda tidak harus mulai menambahkan unit test secara tuntas di mana-mana. Ini akan membawa Anda selamanya dan Anda tidak akan mendapatkan manfaat dari menambahkan tes unit ke 80% kelas yang stabil dan tidak akan berubah dalam waktu dekat (atau tidak begitu dekat). Namun, untuk pekerjaan baru, saya menemukan menggunakan pengembangan TDD dengan kode SEMUA menjadi sangat bermanfaat. Anda tidak hanya berakhir dengan suite dengan tes otomatis ketika Anda selesai, tetapi pengembangan aktual memiliki manfaat besar:

    • Dengan mempertimbangkan testabilitas, Anda akan menulis kode yang kurang digabungkan dan lebih modular
    • Dengan mempertimbangkan kontrak publik Anda sebelum hal lain, Anda akan berakhir dengan antarmuka publik yang jauh lebih bersih
    • Saat Anda menulis kode, memverifikasi fungsionalitas baru membutuhkan milidetik dibandingkan dengan menjalankan seluruh aplikasi Anda dan mencoba memaksa eksekusi di jalur yang benar. Tim saya masih merilis kode penanganan kesalahan yang bahkan belum dieksekusi SEKALI hanya karena mereka tidak bisa mendapatkan kondisi yang tepat untuk terjadi. Sungguh menakjubkan betapa banyak waktu yang kita buang ketika nanti dalam QA kondisi itu bisa terjadi. Dan ya, banyak kode ini adalah apa yang seseorang akan anggap "bukan area untuk banyak perubahan di masa depan setelah pengujian asap dilakukan".
DXM
sumber
1

Ada beberapa metode yang tidak diuji, yaitu tes-tes itu. Namun, ada sesuatu yang bisa dikatakan untuk beberapa tes yang ditambahkan setelah kode awal telah ditulis, seperti kondisi batas dan nilai-nilai lain sehingga mungkin ada beberapa tes pada metode tunggal.

Meskipun Anda dapat menguji kode Anda secara berlebihan, itu biasanya datang ketika seseorang ingin menguji setiap permutasi input yang mungkin tidak terdengar seperti apa yang Anda lakukan. Misalnya, jika Anda memiliki metode yang mengambil karakter, apakah Anda menulis tes untuk setiap nilai yang mungkin dapat dimasukkan? Itu akan menjadi tempat Anda akan melakukan overtesting, IMO.

JB King
sumber
Ahh, baiklah. Bukan itu yang saya lakukan. Saya biasanya hanya berakhir dengan memikirkan situasi yang berbeda. Saya bisa menguji metode saya dengan cara setelah saya membuat tes awal mereka. Saya hanya memastikan tes 'ekstra' itu layak dilakukan, atau jika sudah selesai.
cgasser
Jika Anda bekerja dengan peningkatan yang cukup kecil, Anda biasanya dapat yakin bahwa tes Anda benar-benar berhasil. Dengan kata lain, memiliki tes gagal (untuk alasan yang tepat!) Dengan sendirinya menguji tes. Tetapi tingkat "cukup yakin" itu tidak akan setinggi kode yang diuji.
Frank Shearar
1

Secara umum Anda melakukannya dengan benar.

Tes adalah kode. Jadi, jika Anda dapat meningkatkan tes, silakan dan refactor. Jika Anda berpikir bahwa tes dapat ditingkatkan, silakan dan ubah. Jangan takut untuk mengganti tes dengan yang lebih baik.

Saya sarankan dalam menguji kode Anda, hindari menentukan bagaimana kode seharusnya melakukan apa yang dilakukannya. Tes harus melihat hasil metode. Ini akan membantu refactoring. Beberapa metode tidak perlu diuji secara eksplisit (yaitu getter dan setter sederhana) karena Anda akan menggunakannya untuk memverifikasi hasil tes lain.

Schleis
sumber
Saya menulis tes untuk getter dan setter juga jadi terima kasih untuk tip itu. Itu akan menyelamatkan saya dari beberapa pekerjaan yang tidak dibutuhkan.
cgasser
"Beberapa metode tidak perlu diuji secara eksplisit (yaitu getter dan setter sederhana)" - Anda belum pernah menyalin / menempelkan pengambil dan penyetel dan lupa mengubah nama bidang di belakangnya? Satu hal tentang kode sederhana adalah bahwa itu memerlukan tes sederhana - berapa banyak waktu yang Anda hemat?
pdr
Saya tidak bermaksud bahwa metode ini tidak diuji. Itu hanya diperiksa melalui konfirmasi bahwa metode lain diatur atau selama pengaturan tes yang sebenarnya. Jika pengambil atau penyetel tidak berfungsi dengan benar, pengujian akan gagal karena properti tidak disetel dengan benar. Anda mendapatkan mereka diuji secara gratis, secara implisit.
Schleis
Tes getter dan setter tidak butuh waktu lama, jadi saya mungkin akan terus melakukannya. Namun, saya tidak pernah menyalin dan menempelkan kode saya sehingga saya tidak mengalami masalah itu.
cgasser
0

Pendapat saya tentang TDD adalah bahwa tooling telah menciptakan dunia pengembang gaya 'point and click'. Hanya karena alat membuat rintisan tes untuk setiap metode tidak berarti Anda harus menulis tes untuk setiap metode. Beberapa orang 'mengubah nama' TDD sebagai BDD (pengembangan yang didorong oleh perilaku) di mana tes lebih berbutir besar dan dimaksudkan untuk menguji perilaku kelas, tidak masing-masing metode kecil fiddly.

Jika Anda merancang tes Anda untuk menguji kelas sebagaimana dimaksudkan untuk digunakan, maka Anda mulai mendapatkan beberapa manfaat, terutama ketika Anda mulai menulis tes yang berolahraga sedikit lebih banyak dari setiap metode, terutama ketika Anda mulai menguji interaksi mereka. metode. Saya kira Anda bisa menganggapnya sebagai tes menulis untuk kelas, bukan metode. Bagaimanapun, Anda masih harus menulis 'tes penerimaan' yang menggunakan kombinasi metode untuk memastikan tidak ada kontradiksi atau konflik dalam penggunaannya bersama.

Jangan bingung dengan pengujian TDD - tidak. TDD dirancang agar Anda menulis kode untuk memenuhi kebutuhan Anda, bukan untuk menguji metode. Ini poin yang halus tapi penting yang sering hilang pada orang yang secara buta menulis kode tes untuk setiap metode. Anda harus menulis tes yang memastikan kode Anda melakukan apa yang Anda inginkan, bukan kode yang Anda tulis berfungsi seperti seharusnya.

Ada beberapa tautan bagus ke kanan tentang BDD v TDD. Periksa mereka.

gbjbaanb
sumber
0

Ketika Anda mulai belajar TDD, ya, Anda harus secara membabi buta mengikuti pendekatan dogmatis dari tidak menulis satu baris kode kecuali untuk membuat lulus ujian yang gagal, dan menulis hanya cukup ujian untuk gagal (dan gagal untuk alasan yang tepat / diharapkan) .

Setelah Anda mengetahui apa itu TDD, MAKA Anda dapat memutuskan bahwa hal-hal tertentu tidak layak untuk diuji. Ini adalah pendekatan yang sama yang harus Anda ikuti untuk semuanya, dan seni bela diri Jepang menyebutnya " shuhari ". (Tautan ini juga menjelaskan bagaimana seseorang dapat maju melalui tahap-tahap belajar tanpa guru yang, saya kira, adalah bagaimana kebanyakan orang harus belajar.)

Frank Shearar
sumber
0

Saya percaya bahwa Anda overtesting.

Saya telah berlatih TDD selama bertahun-tahun, dan menurut pengalaman saya, ketika TDD dilakukan secara efektif, Anda mendapatkan dua manfaat utama:

  • Berikan umpan balik yang cepat
  • Aktifkan refactoring

Berikan umpan balik yang cepat

Khususnya dengan bahasa dinamis, saya dapat menjalankan tes yang relevan dalam waktu kurang dari satu detik. Dan saya memiliki pengamat sistem file yang menjalankan tes ini secara otomatis ketika file sumber diubah pada disk. Jadi saya tidak punya waktu tunggu untuk ujian, dan saya langsung tahu apakah kode yang saya tulis sesuai dengan yang diharapkan. Jadi TDD mengarah ke cara kerja yang sangat efisien.

Aktifkan refactoring

Jika Anda memiliki ruang uji yang baik, Anda dapat dengan aman melakukan refactor, karena Anda mendapatkan wawasan baru tentang bagaimana sistem harus dirancang.

Rangkaian pengujian yang baik memungkinkan Anda untuk memindahkan tanggung jawab dalam kode Anda, dan masih memiliki keyakinan bahwa kode tersebut berfungsi seperti yang diharapkan setelah pindah. Dan Anda harus dapat melakukan ini dengan sedikit perubahan pada kode tes.

Jika Anda menulis tes untuk setiap metode dalam sistem Anda, kemungkinan besar Anda tidak dapat dengan mudah memperbaiki kode Anda, setiap refactor kode Anda akan membutuhkan perubahan besar pada kode tes. Dan bisakah Anda bahkan yakin bahwa kode pengujian masih berfungsi seperti yang diharapkan? Atau apakah Anda secara tidak sengaja memasukkan bug dalam kode pengujian, yang akibatnya mengarah ke bug dalam kode produksi?

Namun, jika Anda, seperti juga disarankan dalam jawaban pdr , fokus pada perilaku daripada metode saat menulis tes, Anda akan memiliki tes yang akan memerlukan perubahan jauh lebih sedikit ketika refactoring sistem.

Atau seperti yang dikatakan Ian Cooper dalam presentasi ini (saya kutip dari ingatan, jadi mungkin tidak dikutip dengan benar):

Alasan Anda untuk menulis tes baru harus menambahkan perilaku baru, bukan menambahkan kelas baru

Pete
sumber
-2

Anda harus menguji setiap metode publik .

Tangkapan di sini adalah bahwa jika metode publik Anda sangat kecil, Anda mungkin mengungkapkan terlalu banyak informasi. Praktek umum mengungkapkan setiap properti sebagai getXXX()benar - benar merusak enkapsulasi.

Jika metode publik Anda benar-benar perilaku kelas, maka Anda harus mengujinya. Jika tidak, itu bukan metode publik yang baik.

EDIT: jawaban pdr jauh lebih lengkap daripada milikku.

Chip
sumber