Apakah cukup untuk menggunakan tes penerimaan dan integrasi daripada tes unit?

62

Pengantar singkat untuk pertanyaan ini. Saya telah menggunakan TDD sekarang dan belakangan ini BDD selama lebih dari satu tahun sekarang. Saya menggunakan teknik seperti mengejek untuk membuat menulis tes saya lebih efisien. Akhir-akhir ini saya telah memulai proyek pribadi untuk menulis sedikit program pengelolaan uang untuk diri saya sendiri. Karena saya tidak punya kode warisan, itu adalah proyek yang sempurna untuk memulai dengan TDD. Sayangnya saya tidak mengalami banyak kesenangan dari TDD. Itu bahkan merusak kesenangan saya sehingga saya menyerah pada proyek.

Apa masalahnya? Yah, saya telah menggunakan pendekatan seperti TDD untuk membiarkan tes / persyaratan berevolusi desain program. Masalahnya adalah lebih dari setengah waktu pengembangan untuk tes menulis / refactor. Jadi pada akhirnya saya tidak ingin menerapkan lebih banyak fitur karena saya perlu refactor dan menulis ke banyak tes.

Di tempat kerja saya memiliki banyak kode warisan. Di sini saya menulis lebih banyak dan lebih banyak tes integrasi dan penerimaan dan lebih sedikit tes unit. Ini tampaknya bukan pendekatan yang buruk karena bug sebagian besar terdeteksi oleh tes penerimaan dan integrasi.

Gagasan saya adalah, pada akhirnya saya bisa menulis lebih banyak tes integrasi dan penerimaan daripada tes unit. Seperti yang saya katakan untuk mendeteksi bug, tes unit tidak lebih baik dari tes integrasi / penerimaan. Tes unit juga baik untuk desain. Karena saya sering menulis banyak dari mereka, kelas saya selalu dirancang agar dapat diuji dengan baik. Selain itu, pendekatan untuk membiarkan tes / persyaratan memandu desain dalam banyak kasus mengarah ke desain yang lebih baik. Keuntungan terakhir dari unit test adalah mereka lebih cepat. Saya telah menulis cukup tes integrasi untuk mengetahui, bahwa mereka bisa hampir secepat unit test.

Setelah saya mencari melalui web saya menemukan bahwa ada ide yang sangat mirip dengan saya yang disebutkan di sana - sini . Apa pendapatmu tentang ide ini?

Sunting

Menanggapi pertanyaan-pertanyaan satu contoh di mana desainnya bagus, tetapi saya membutuhkan refactoring besar untuk persyaratan berikutnya:

Pada awalnya ada beberapa persyaratan untuk menjalankan perintah tertentu. Saya menulis parser perintah yang dapat diperpanjang - yang mem-parsing perintah dari beberapa jenis command prompt dan memanggil yang benar pada model. Hasilnya diwakili dalam kelas model tampilan: Desain pertama

Tidak ada yang salah di sini. Semua kelas independen satu sama lain dan saya dapat dengan mudah menambahkan perintah baru, menampilkan data baru.

Persyaratan berikutnya adalah, bahwa setiap perintah harus memiliki representasi tampilan sendiri - semacam pratinjau hasil perintah. Saya mendesain ulang program untuk mencapai desain yang lebih baik untuk persyaratan baru: Desain kedua

Ini juga bagus karena sekarang setiap perintah memiliki model tampilan sendiri dan oleh karena itu pratinjau sendiri.

Masalahnya adalah, bahwa parser perintah diubah untuk menggunakan parsing berbasis token dari perintah dan dilucuti dari kemampuannya untuk mengeksekusi perintah. Setiap perintah memiliki model tampilan sendiri dan model tampilan data hanya mengetahui model tampilan perintah saat ini daripada mengetahui data yang harus ditampilkan.

Yang ingin saya ketahui pada saat ini adalah, jika desain baru tidak melanggar persyaratan yang ada. Saya tidak perlu mengubah APA SAJA dari tes penerimaan saya. Saya harus memperbaiki atau menghapus hampir SETIAP unit test, yang merupakan tumpukan pekerjaan.

Apa yang ingin saya tunjukkan di sini adalah situasi umum yang sering terjadi selama pengembangan. Tidak ada masalah dengan desain lama atau baru, mereka hanya berubah secara alami dengan persyaratan - bagaimana saya memahaminya, ini adalah salah satu keunggulan TDD, bahwa desain berkembang.

Kesimpulan

Terima kasih atas semua jawaban dan diskusi. Dalam ringkasan diskusi ini saya telah memikirkan suatu pendekatan yang akan saya uji dengan proyek saya berikutnya.

  • Pertama-tama saya menulis semua tes sebelum menerapkan hal seperti yang selalu saya lakukan.
  • Untuk persyaratan, pada awalnya saya menulis beberapa tes penerimaan yang menguji seluruh program. Lalu saya menulis beberapa tes integrasi untuk komponen di mana saya perlu menerapkan persyaratan. Jika ada komponen yang bekerja sama erat dengan komponen lain untuk mengimplementasikan persyaratan ini, saya juga akan menulis beberapa tes integrasi di mana kedua komponen diuji bersama. Terakhir tetapi tidak kalah pentingnya jika saya harus menulis algoritma atau kelas lain dengan permutasi tinggi - misalnya serializer - saya akan menulis unit test untuk kelas khusus ini. Semua kelas lain tidak diuji tetapi tes unit apa pun.
  • Untuk bug prosesnya dapat disederhanakan. Biasanya bug disebabkan oleh satu atau dua komponen. Dalam hal ini saya akan menulis satu tes integrasi untuk komponen yang menguji bug. Jika terkait dengan algoritma, saya hanya akan menulis unit test. Jika tidak mudah untuk mendeteksi komponen di mana bug terjadi, saya akan menulis tes penerimaan untuk menemukan bug - ini harus menjadi pengecualian.
Yggdrasil
sumber
Pertanyaan-pertanyaan ini tampaknya lebih membahas masalah mengapa menulis tes sama sekali. Saya ingin mendiskusikan apakah menulis tes fungsional daripada tes unit mungkin merupakan pendekatan yang lebih baik.
Yggdrasil
per bacaan saya, jawaban dalam pertanyaan rangkap terutama tentang kapan tes non- unit lebih masuk akal
nyamuk
Tautan pertama itu sendiri merupakan duplikat. Pikirkan maksud Anda: programmers.stackexchange.com/questions/66480/…
Robbie Dee
Jawaban dari tautan dari Robbie Dee bahkan lebih tentang mengapa harus diuji sama sekali.
Yggdrasil

Jawaban:

37

Ini membandingkan jeruk dan apel.

Tes integrasi, tes penerimaan, tes unit, tes perilaku - semuanya adalah tes dan semuanya akan membantu Anda meningkatkan kode Anda, tetapi semuanya juga sangat berbeda.

Saya akan membahas masing-masing tes yang berbeda menurut pendapat saya dan mudah-mudahan menjelaskan mengapa Anda membutuhkan perpaduan dari semuanya:

Tes integrasi:

Sederhananya, uji apakah bagian komponen yang berbeda dari sistem Anda terintegrasi dengan benar - misalnya - mungkin Anda mensimulasikan permintaan layanan web dan memeriksa apakah hasilnya kembali. Saya biasanya akan menggunakan data statis (ish) nyata dan dependensi mengejek untuk memastikan bahwa itu dapat diverifikasi secara konsisten.

Tes penerimaan:

Tes penerimaan harus langsung berkorelasi dengan kasus penggunaan bisnis. Itu bisa sangat besar ("perdagangan dikirimkan dengan benar") atau kecil ("filter berhasil memfilter daftar") - tidak masalah; yang penting adalah bahwa itu harus secara eksplisit dikaitkan dengan kebutuhan pengguna tertentu. Saya suka fokus pada hal ini untuk pengembangan yang digerakkan oleh tes karena itu berarti kami memiliki manual referensi yang baik untuk cerita pengguna untuk dev dan qa untuk diverifikasi.

Tes unit:

Untuk unit kecil yang terpisah dari fungsionalitas yang mungkin atau mungkin tidak membentuk cerita pengguna tersendiri dengan sendirinya - misalnya, cerita pengguna yang mengatakan bahwa kami mengambil semua pelanggan ketika kami mengakses halaman web tertentu dapat menjadi tes penerimaan (simulasikan mengenai web halaman dan memeriksa respons) tetapi juga dapat berisi beberapa tes unit (memverifikasi bahwa izin keamanan diperiksa, memverifikasi bahwa koneksi database menanyakan dengan benar, memverifikasi bahwa kode apa pun yang membatasi jumlah hasil dieksekusi dengan benar) - ini semua "unit test" itu bukan tes penerimaan lengkap.

Tes perilaku:

Tetapkan apa yang seharusnya mengalir dari aplikasi jika input tertentu. Misalnya, "ketika koneksi tidak dapat dibuat, verifikasi bahwa sistem mencoba kembali koneksi." Sekali lagi, ini tidak mungkin menjadi tes penerimaan penuh tetapi masih memungkinkan Anda untuk memverifikasi sesuatu yang bermanfaat.

Ini semua menurut saya melalui banyak pengalaman menulis tes; Saya tidak suka fokus pada pendekatan buku teks - melainkan, fokus pada apa yang memberikan nilai tes Anda.

Michael
sumber
Dalam definisi Anda, saya berasumsi apa yang saya maksud adalah menulis lebih banyak tes perilaku (?) Daripada tes unit. Tes unit untuk saya adalah tes yang menguji satu kelas dengan semua dependensi diejek. Ada kasus di mana tes unit paling berguna. Itu misalnya ketika saya menulis algoritma yang kompleks. Lalu saya punya banyak contoh dengan hasil yang diharapkan dari algoritma. Saya ingin menguji ini pada tingkat unit karena sebenarnya lebih cepat daripada tes perilaku. Saya tidak melihat nilai tes kelas pada tingkat unit yang hanya memiliki tangan penuh jalur melalui kelas yang dapat dengan mudah diuji oleh tes perilaku.
Yggdrasil
14
Saya pribadi berpikir tes penerimaan adalah yang paling penting, tes perilaku penting ketika menguji hal-hal seperti komunikasi, reliabilitas dan kasus kesalahan dan unit test penting ketika menguji fitur kompleks kecil (algoritma akan menjadi contoh yang baik untuk ini)
Michael
Saya tidak begitu tegas dengan terminologi Anda. Kami memprogram suite pemrograman. Di sana saya bertanggung jawab untuk editor grafis. Tes saya menguji editor dengan layanan mengejek dari seluruh rangkaian dan dengan UI mengejek. Tes macam apa itu?
Yggdrasil
1
Tergantung pada apa yang Anda uji - apakah Anda menguji fitur bisnis (tes penerimaan)? Apakah Anda menguji integrasi (tes integrasi)? Apakah Anda menguji apa yang terjadi ketika Anda mengklik tombol (tes perilaku)? Apakah Anda menguji algoritme (unit test)?
Michael
4
"Saya tidak suka fokus pada pendekatan buku teks - sebaliknya, fokus pada apa yang memberikan nilai tes Anda" Oh, benar sekali! Pertanyaan pertama yang selalu ditanyakan adalah "masalah apa yang saya pecahkan dengan melakukan ini?". Dan proyek yang berbeda mungkin memiliki masalah yang berbeda untuk dipecahkan!
Laurent Bourgault-Roy
40

TL; DR: Selama memenuhi kebutuhan Anda, ya.

Saya telah melakukan pengembangan Acceptance Test Driven Development (ATDD) selama bertahun-tahun sekarang. Itu bisa sangat sukses. Ada beberapa hal yang perlu diperhatikan.

  • Tes unit benar-benar membantu menegakkan IOC. Tanpa unit test, tanggung jawab ada pada pengembang untuk memastikan mereka memenuhi persyaratan kode yang ditulis dengan baik (sejauh unit test mendorong kode yang ditulis dengan baik)
  • Mereka bisa lebih lambat dan memiliki kegagalan palsu jika Anda benar-benar menggunakan sumber daya yang biasanya diejek.
  • Tes tidak menunjukkan masalah spesifik seperti yang akan dilakukan unit test. Anda perlu melakukan lebih banyak penyelidikan untuk memperbaiki kegagalan tes.

Sekarang manfaatnya

  • Cakupan tes yang jauh lebih baik, mencakup poin integrasi.
  • Memastikan sistem secara keseluruhan memenuhi kriteria penerimaan, yang merupakan inti dari pengembangan perangkat lunak.
  • Membuat refaktor besar jauh lebih mudah, lebih cepat, dan lebih murah.

Seperti biasa, terserah Anda untuk melakukan analisis dan mencari tahu apakah praktik ini sesuai dengan situasi Anda. Tidak seperti banyak orang, saya tidak berpikir ada jawaban yang tepat dan ideal. Itu akan tergantung pada kebutuhan dan persyaratan Anda.

dietbuddha
sumber
8
Poin luar biasa. Terlalu mudah untuk menjadi kadet ruang kecil tentang pengujian dan menulis ratusan kasus untuk mendapatkan kepuasan hangat ketika "berlalu" dengan warna terbang. Sederhananya: jika perangkat lunak Anda tidak melakukan apa yang perlu dilakukan dari sudut pandang USER, Anda telah gagal tes pertama dan yang paling penting.
Robbie Dee
Poin yang bagus untuk menunjukkan dengan tepat masalah spesifik. Jika saya memiliki persyaratan yang sangat besar saya menulis tes penerimaan yang menguji seluruh sistem dan daripada menulis tes yang menguji sub tugas dari komponen tertentu dari sistem untuk mencapai persyaratan. Dengan ini saya dapat menunjukkan dalam banyak kasus komponen di mana cacat terletak.
Yggdrasil
2
"Tes unit membantu menegakkan IOC"?!? Saya yakin Anda bermaksud DI di sana, bukan IoC, tetapi bagaimana pun, mengapa seseorang ingin menegakkan penggunaan DI? Secara pribadi, saya menemukan bahwa dalam praktiknya DI mengarah ke non-objek (pemrograman gaya prosedural).
Rogério
Bisakah Anda memberikan masukan Anda pada opsi (terbaik IMO) untuk melakukan KEDUA integrasi dan pengujian unit vs argumen hanya melakukan pengujian integrasi? Jawaban Anda di sini bagus, tetapi tampaknya membingkai hal-hal ini sebagai saling eksklusif, yang saya tidak percaya mereka.
starmandeluxe
@starmandeluxe Mereka memang tidak saling eksklusif. Sebaliknya itu adalah pertanyaan tentang nilai yang ingin Anda peroleh dari pengujian. Saya akan menguji unit di mana saja di mana nilainya melebihi biaya pengembangan / dukungan penulisan tes unit. ex. Saya pasti akan menguji fungsi bunga majemuk dalam aplikasi keuangan.
dietbuddha
18

Yah, saya telah menggunakan pendekatan seperti TDD untuk membiarkan tes / persyaratan berevolusi desain program. Masalahnya adalah lebih dari setengah waktu pengembangan untuk tes menulis / refactor

Tes unit bekerja paling baik ketika antarmuka publik dari komponen yang digunakan tidak terlalu sering berubah. Ini berarti, ketika komponen sudah dirancang dengan baik (misalnya, mengikuti prinsip SOLID).

Jadi mempercayai desain yang baik hanya "berevolusi" dari "melempar" banyak tes unit pada suatu komponen adalah kekeliruan. TDD bukan "guru" untuk desain yang baik, itu hanya dapat membantu sedikit untuk memverifikasi bahwa aspek-aspek tertentu dari desain itu baik (terutama testabilitas).

Ketika kebutuhan Anda berubah, dan Anda harus mengubah bagian dalam komponen, dan ini akan merusak 90% dari pengujian unit Anda, jadi Anda harus sering-sering refactor, maka desain yang paling mungkin tidak begitu baik.

Jadi saran saya adalah: pikirkan desain komponen yang telah Anda buat, dan bagaimana Anda dapat membuatnya lebih mengikuti prinsip buka / tutup. Ide yang terakhir adalah untuk memastikan fungsionalitas komponen Anda dapat diperpanjang kemudian tanpa mengubahnya (dan dengan demikian tidak melanggar API komponen yang digunakan oleh unit test Anda). Komponen-komponen seperti itu dapat (dan harus) dicakup oleh tes unit test, dan pengalaman itu seharusnya tidak menyakitkan seperti yang telah Anda gambarkan.

Ketika Anda tidak dapat segera membuat desain seperti itu, tes penerimaan dan integrasi mungkin memang merupakan awal yang lebih baik.

EDIT: Terkadang desain komponen Anda bisa baik-baik saja, tetapi desain unit test Anda dapat menyebabkan masalah . Contoh sederhana: Anda ingin menguji metode "MyMethod" dari kelas X dan menulis

    var x= new X();
    Assert.AreEqual("expected value 1" x.MyMethod("value 1"));
    Assert.AreEqual("expected value 2" x.MyMethod("value 2"));
    // ...
    Assert.AreEqual("expected value 500" x.MyMethod("value 500"));

(anggap nilai memiliki semacam makna).

Asumsikan lebih lanjut, bahwa dalam kode produksi hanya ada satu panggilan X.MyMethod. Sekarang, untuk persyaratan baru, metode "MyMethod" membutuhkan parameter tambahan (misalnya, sesuatu seperti context), yang tidak dapat dihilangkan. Tanpa tes unit, seseorang harus memperbaiki kode panggilan hanya di satu tempat. Dengan tes unit, seseorang harus merefleksikan 500 tempat.

Tetapi penyebabnya di sini bukanlah unit test itu sendiri, itu hanya fakta bahwa panggilan yang sama untuk "X.Metode" diulangi lagi dan lagi, tidak secara ketat mengikuti prinsip "Jangan Ulangi Diri Sendiri (KERING). Jadi solusinya di sini adalah untuk meletakkan data pengujian dan nilai-nilai yang diharapkan terkait dalam daftar dan menjalankan panggilan ke "MyMethod" dalam satu lingkaran (atau, jika alat pengujian mendukung apa yang disebut "tes drive data", untuk menggunakan fitur itu). Ini mengurangi jumlah tempat yang akan diubah dalam tes unit ketika tanda tangan metode berubah menjadi 1 (berlawanan dengan 500).

Dalam kasus dunia nyata Anda, situasinya mungkin lebih kompleks, tetapi saya harap Anda mendapatkan idenya - ketika pengujian unit Anda menggunakan API komponen yang tidak Anda ketahui jika dapat berubah, pastikan Anda mengurangi jumlahnya panggilan ke API tersebut seminimal mungkin.

Doc Brown
sumber
"Ini berarti, ketika komponen sudah dirancang dengan baik.": Saya setuju dengan Anda, tetapi bagaimana komponen tersebut dapat dirancang jika Anda menulis tes sebelum menulis kode, dan kode adalah desain? Setidaknya begitulah cara saya memahami TDD.
Giorgio
2
@Iorgio: sebenarnya, tidak masalah jika Anda menulis tes pertama atau lambat. Desain berarti membuat keputusan tentang tanggung jawab komponen, tentang antarmuka publik, tentang dependensi (langsung atau disuntikkan), tentang waktu berjalan atau kompilasi perilaku waktu, tentang kemampuan berubah-ubah, tentang nama, tentang aliran data, aliran kontrol, lapisan dll. Bagus desain juga berarti menunda beberapa keputusan ke titik waktu terbaru yang memungkinkan. Uji unit dapat menunjukkan secara tidak langsung jika desain Anda baik-baik saja: jika Anda sering melakukan refactor banyak setelah itu ketika persyaratan berubah, itu mungkin tidak.
Doc Brown
@Iorgio: contoh mungkin memperjelas: katakan Anda memiliki komponen X dengan metode "MyMethod" dan 2 parameter. Menggunakan TDD, Anda menulis X x= new X(); AssertTrue(x.MyMethod(12,"abc"))sebelum benar-benar menerapkan metode ini. Menggunakan desain dimuka, Anda dapat menulis class X{ public bool MyMethod(int p, string q){/*...*/}}terlebih dahulu, dan menulis tes nanti. Dalam kedua kasus, Anda telah membuat keputusan desain yang sama. Jika keputusan itu baik atau buruk, TDD tidak akan memberi tahu Anda.
Doc Brown
1
Saya setuju dengan Anda: Saya agak skeptis ketika saya melihat TDD diterapkan secara membabi buta dengan asumsi bahwa itu akan secara otomatis menghasilkan desain yang baik. Selain itu, kadang-kadang TDD menghalangi jika desainnya belum jelas: Saya dipaksa untuk menguji detail sebelum saya memiliki gambaran tentang apa yang saya lakukan. Jadi, jika saya mengerti dengan benar, kami setuju. Saya pikir (1) unit testing membantu memverifikasi desain tetapi desain adalah kegiatan yang terpisah, dan (2) TDD tidak selalu merupakan solusi terbaik karena Anda perlu mengatur ide-ide Anda sebelum mulai menulis tes dan TDD dapat memperlambat Anda dalam ini.
Giorgio
1
Singkatnya, unit test dapat menunjukkan kekurangan dalam desain internal suatu komponen. Antarmuka, kondisi sebelum dan sesudah harus diketahui sebelumnya, jika tidak, Anda tidak dapat membuat unit test. Jadi desain komponen apa yang perlu dilakukan komponen sebelum unit test dapat ditulis. Bagaimana ini dilakukan - desain level bawah, desain detail atau desain bagian dalam atau apa pun yang Anda inginkan - dapat terjadi setelah unit test ditulis.
Maarten Bodewes
9

Ya, tentunya.

Pertimbangkan ini:

  • unit test adalah bagian kecil, pengujian yang ditargetkan yang melatih bagian kecil kode. Anda menulis banyak dari mereka untuk mencapai cakupan kode yang layak, sehingga semua (atau sebagian besar bit canggung) diuji.
  • tes integrasi adalah bagian besar, pengujian luas yang melatih permukaan besar kode Anda. Anda menulis beberapa dari mereka untuk mencapai cakupan kode yang layak, sehingga semua (atau sebagian besar bit canggung) diuji.

Lihat perbedaan keseluruhan ....

Isu ini merupakan salah satu cakupan kode, jika Anda dapat mencapai tes penuh dari semua kode Anda menggunakan pengujian integrasi / penerimaan, maka tidak ada masalah. Kode Anda diuji. Itulah tujuannya.

Saya pikir Anda mungkin perlu mencampurnya, karena setiap proyek berbasis TDD akan memerlukan beberapa pengujian integrasi hanya untuk memastikan bahwa semua unit benar-benar bekerja dengan baik bersama-sama (saya tahu dari pengalaman bahwa 100% unit kode basis yang diuji tidak selalu bekerja ketika Anda menempatkan mereka semua!)

Masalahnya benar-benar turun ke kemudahan pengujian, men-debug kegagalan, dan memperbaikinya. Beberapa orang menemukan unit test mereka sangat bagus dalam hal ini, mereka kecil dan sederhana dan kegagalan mudah dilihat, tetapi kerugiannya adalah Anda harus mengatur ulang kode Anda agar sesuai dengan alat tes unit, dan menulis sangat banyak dari mereka. Tes integrasi lebih sulit untuk ditulis untuk mencakup banyak kode, dan Anda mungkin harus menggunakan teknik seperti masuk untuk men-debug setiap kegagalan (meskipun, saya akan mengatakan Anda harus melakukan ini, Anda tidak dapat unit kegagalan pengujian ketika di tempat!).

Bagaimanapun, Anda masih mendapatkan kode yang diuji, Anda hanya perlu memutuskan mekanisme mana yang lebih cocok untuk Anda. (Saya akan menggunakan sedikit campuran, unit menguji algoritma yang kompleks, dan mengintegrasikan menguji sisanya).

gbjbaanb
sumber
7
Tidak sepenuhnya benar ... Dengan tes integrasi, dimungkinkan untuk memiliki dua komponen yang sama-sama bermasalah, tetapi bug mereka dibatalkan dalam tes integrasi. Tidak masalah sampai pengguna akhir menggunakannya dengan cara yang hanya menggunakan salah satu komponen ini ...
Michael Shaw
1
Cakupan kode! = Diuji - selain bug yang membatalkan satu sama lain, bagaimana dengan skenario yang tidak pernah Anda pikirkan? Pengujian integrasi baik untuk pengujian jalur senang, tetapi saya jarang melihat pengujian integrasi yang memadai ketika semuanya tidak berjalan dengan baik.
Michael
4
@ Poltemy Saya pikir kelangkaan 2 komponen buggy yang saling membatalkan jauh, jauh lebih rendah dari 2 komponen kerja yang saling mengganggu.
gbjbaanb
2
@Michael maka Anda hanya belum melakukan upaya yang cukup dalam pengujian, saya memang mengatakan itu lebih sulit untuk melakukan pengujian integrasi yang baik karena tes harus jauh lebih rinci. Anda dapat menyediakan data buruk dalam tes integrasi semudah yang Anda bisa dalam tes unit. Pengujian integrasi! = Jalan bahagia. Ini tentang menjalankan kode sebanyak yang Anda bisa, itu sebabnya ada alat cakupan kode yang menunjukkan seberapa banyak kode Anda telah dilakukan.
gbjbaanb
1
@Michael Ketika saya menggunakan alat seperti Mentimun atau SpecFlow dengan benar, saya dapat membuat tes integrasi yang juga menguji pengecualian dan situasi ekstrem secepat pengujian unit. Tetapi saya setuju jika satu kelas memiliki banyak permutasi, saya lebih suka menulis unit test untuk kelas ini. Tetapi ini akan lebih jarang terjadi daripada memiliki kelas dengan hanya beberapa jalur.
Yggdrasil
2

Saya pikir itu ide yang mengerikan.

Karena tes penerimaan dan tes integrasi menyentuh bagian yang lebih luas dari kode Anda untuk menguji target tertentu, mereka akan memerlukan lebih banyak refactoring dari waktu ke waktu, tidak kurang. Lebih buruk lagi, karena mereka mencakup bagian luas dari kode, mereka meningkatkan waktu yang Anda habiskan untuk melacak penyebab root karena Anda memiliki area yang lebih luas untuk dicari.

Tidak, Anda biasanya harus menulis tes unit lebih banyak kecuali Anda memiliki aplikasi aneh di 90% UI atau sesuatu yang aneh untuk pengujian unit. Rasa sakit yang Anda hadapi bukan dari tes unit, tetapi melakukan tes pengembangan pertama. Secara umum, Anda hanya harus menghabiskan 1/3 waktu Anda di sebagian besar tes menulis. Lagi pula, mereka ada di sana untuk melayani Anda, bukan sebaliknya.

Telastyn
sumber
2
Daging sapi utama yang saya dengar melawan TDD adalah mengganggu aliran pembangunan alami dan memberlakukan pemrograman defensif sejak awal. Jika seorang programmer sudah di bawah tekanan waktu, mereka cenderung ingin hanya memotong kode dan memolesnya nanti. Tentu saja, memenuhi tenggat waktu sewenang-wenang dengan kode kereta adalah ekonomi palsu.
Robbie Dee
2
Memang, terutama karena "poles nanti" sepertinya tidak pernah benar-benar terjadi - setiap ulasan yang saya lakukan di mana pengembang mendorong "itu harus keluar, kami akan melakukannya nanti" ketika kita semua tahu, itu tidak akan terjadi - teknis hutang = pengembang bangkrut menurut saya.
Michael
3
Jawabannya masuk akal bagi saya, tidak tahu mengapa ada begitu banyak kekurangan. Mengutip Mike Cohn: "Pengujian unit harus menjadi dasar dari strategi otomasi pengujian yang solid dan dengan demikian mewakili bagian terbesar dari piramida. Tes unit otomatis sangat bagus karena mereka memberikan data spesifik kepada seorang programmer — ada bug dan ada pada baris 47 " mountaingoatsoftware.com/blog/…
guillaume31
4
@ guillaume31 Hanya karena beberapa pria pernah berkata bahwa mereka baik tidak berarti, bahwa mereka baik. Dalam pengalaman saya bug TIDAK terdeteksi oleh unit test, karena mereka diubah ke persyaratan baru di muka. Juga sebagian besar bug adalah bug integrasi. Karenanya saya mendeteksi sebagian besar dari mereka dengan tes integrasi saya.
Yggdrasil
2
@Yggdrasil Saya juga bisa mengutip Martin Fowler: "Jika Anda mendapatkan kegagalan dalam tes tingkat tinggi, Anda tidak hanya memiliki bug dalam kode fungsional Anda, Anda juga memiliki unit test yang hilang". martinfowler.com/bliki/TestPyramid.html Lagi pula, jika tes integrasi saja bekerja untuk Anda, maka baik-baik saja. Pengalaman saya adalah bahwa, meskipun diperlukan, mereka lebih lambat, memberikan pesan kegagalan yang kurang tepat dan kurang bermanuver (lebih kombinatorial) daripada tes unit. Juga, saya cenderung kurang bukti di masa depan ketika saya menulis tes integrasi - alasan tentang pra-(salah?) Skenario yang disusun daripada kebenaran objek per se.
guillaume31
2

"Kemenangan" dengan TDD, adalah bahwa setelah tes telah ditulis, mereka dapat otomatis. Sisi lain adalah bahwa ia dapat mengkonsumsi sebagian besar waktu pengembangan. Apakah ini benar-benar memperlambat seluruh proses turun adalah diperdebatkan Argumennya adalah bahwa pengujian dimuka mengurangi jumlah kesalahan yang harus diperbaiki pada akhir siklus pengembangan.

Di sinilah BDD muncul karena perilaku dapat dimasukkan dalam pengujian unit sehingga prosesnya secara definisi kurang abstrak dan lebih nyata.

Jelas, jika jumlah waktu tak terbatas tersedia, Anda akan melakukan sebanyak mungkin pengujian berbagai varietas. Namun, waktu pada umumnya terbatas dan pengujian berkelanjutan hanya efektif biaya sampai titik tertentu.

Ini semua mengarah pada kesimpulan bahwa tes yang memberikan nilai paling banyak harus berada di depan proses. Ini sendiri tidak secara otomatis mendukung satu jenis pengujian di atas yang lain - lebih dari itu setiap kasus harus diambil berdasarkan kelebihannya.

Jika Anda menulis widget baris perintah untuk penggunaan pribadi, Anda terutama akan tertarik pada pengujian unit. Sedangkan layanan web mengatakan, akan membutuhkan sejumlah besar pengujian integrasi / perilaku.

Sementara sebagian besar jenis tes berkonsentrasi pada apa yang bisa disebut "garis balap" yaitu menguji apa yang dibutuhkan oleh bisnis saat ini, pengujian unit sangat baik untuk menghilangkan bug halus yang dapat muncul pada fase pengembangan selanjutnya. Karena ini adalah manfaat yang tidak dapat diukur dengan mudah, ini sering diabaikan.

Robbie Dee
sumber
1
Saya menulis tes saya di muka dan saya menulis cukup tes untuk menutupi sebagian besar kesalahan. Adapun bug yang muncul kemudian. Ini kedengarannya bagi saya, bahwa suatu persyaratan telah berubah atau suatu persyaratan baru ikut bermain. Dari tes integrasi / perilaku perlu diubah, ditambahkan. Jika kemudian bug ditampilkan dalam persyaratan lama, tes saya untuk ini akan menunjukkannya. Adapun otomasi. semua tes saya berjalan sepanjang waktu.
Yggdrasil
Contoh yang saya pikirkan dalam paragraf terakhir adalah di mana katakanlah, perpustakaan digunakan secara eksklusif oleh satu aplikasi tetapi kemudian ada persyaratan bisnis untuk menjadikan ini perpustakaan tujuan umum. Dalam hal ini mungkin lebih baik melayani Anda untuk memiliki setidaknya beberapa pengujian unit daripada menulis tes integrasi / perilaku baru untuk setiap sistem yang Anda lampirkan ke perpustakaan.
Robbie Dee
2
Pengujian otomatis dan pengujian unit adalah hal yang sepenuhnya ortogonal. Setiap proyek yang menghargai diri sendiri akan memiliki integrasi otomatis dan pengujian fungsional. Memang, Anda tidak sering melihat tes unit manual, tetapi mereka bisa ada (pada dasarnya tes unit manual adalah utilitas pengujian untuk beberapa fungsi tertentu).
Jan Hudec
Ya memang. Telah ada pasar yang berkembang untuk alat pihak ke-3 otomatis yang ada di luar lingkup pengembangan untuk beberapa waktu yang cukup lama.
Robbie Dee
1

Keuntungan terakhir dari unit test adalah mereka lebih cepat. Saya telah menulis cukup tes integrasi untuk mengetahui, bahwa mereka bisa hampir secepat unit test.

Ini adalah titik kunci, dan bukan hanya "keunggulan terakhir". Ketika proyek semakin besar dan lebih besar, tes penerimaan integrasi Anda menjadi semakin lambat. Dan di sini, maksud saya sangat lambat sehingga Anda akan berhenti mengeksekusi mereka.

Tentu saja, tes unit menjadi lebih lambat juga, tetapi mereka masih lebih dari urutan besarnya lebih cepat. Misalnya, dalam proyek saya sebelumnya (c ++, sekitar 600 kLOC, 4000 unit test dan 200 tes integrasi), butuh sekitar satu menit untuk menjalankan semua dan lebih dari 15 untuk menjalankan tes integrasi. Untuk membangun dan menjalankan tes unit untuk bagian yang sedang diubah, akan memakan waktu rata-rata kurang dari 30 detik. Ketika Anda bisa melakukannya dengan sangat cepat, Anda pasti ingin melakukannya setiap saat.

Hanya untuk memperjelas: Saya tidak mengatakan tidak menambahkan tes integrasi dan penerimaan, tetapi sepertinya Anda melakukan TDD / BDD dengan cara yang salah.

Tes unit juga baik untuk desain.

Ya, merancang dengan testability dalam pikiran akan membuat desain lebih baik.

Masalahnya adalah lebih dari setengah waktu pengembangan untuk tes menulis / refactor. Jadi pada akhirnya saya tidak ingin menerapkan lebih banyak fitur karena saya perlu refactor dan menulis ke banyak tes.

Nah, ketika persyaratan berubah, Anda harus mengubah kode. Saya akan memberitahu Anda tidak menyelesaikan pekerjaan Anda jika Anda tidak menulis tes unit. Tapi ini tidak berarti Anda harus memiliki cakupan 100% dengan unit test - itu bukan tujuannya. Beberapa hal (seperti GUI, atau mengakses file, ...) bahkan tidak dimaksudkan untuk diuji unit.

Hasil dari ini adalah kualitas kode yang lebih baik, dan lapisan pengujian lainnya. Saya akan mengatakan itu layak.


Kami juga memiliki beberapa tes penerimaan 1000-an, dan itu akan memakan waktu seminggu penuh untuk melaksanakan semua.

BЈовић
sumber
1
Sudahkah Anda melihat contoh saya? Ini terjadi setiap saat. Intinya adalah, ketika saya mengimplementasikan fitur baru saya mengubah / menambahkan tes unit sehingga mereka menguji fitur baru - karena itu tidak ada uji unit yang akan rusak. Dalam kebanyakan kasus, saya memiliki efek samping dari perubahan yang tidak terdeteksi oleh unit test - karena lingkungannya dipermainkan. Dalam pengalaman saya karena ini tidak ada unit test yang pernah memberi tahu saya bahwa saya telah merusak fitur yang ada. Itu selalu tes integrasi dan penerimaan yang menunjukkan kesalahan saya.
Yggdrasil
Adapun waktu eksekusi. Dengan aplikasi yang berkembang, saya memiliki sebagian besar komponen yang semakin banyak. Jika tidak, saya telah melakukan kesalahan. Ketika saya mengimplementasikan fitur baru, sebagian besar hanya dalam jumlah komponen yang terbatas. Saya menulis satu atau lebih tes penerimaan dalam lingkup seluruh aplikasi - yang bisa lambat. Selain itu saya menulis tes yang sama dari sudut pandang komponen - tes ini cepat, karena komponen biasanya cepat. Saya dapat menjalankan tes komponen setiap saat, karena mereka cukup cepat.
Yggdrasil
@ Yggdrasil Seperti yang saya katakan, unit test tidak semuanya perkasa, tetapi mereka biasanya merupakan lapisan pertama pengujian, karena mereka adalah yang tercepat. Tes lain juga bermanfaat, dan harus digabungkan.
BЈовић
1
Hanya karena mereka lebih cepat tidak berarti, bahwa mereka harus digunakan hanya karena ini atau karena itu umum untuk menulis itu. Seperti yang saya katakan, unit test saya tidak rusak - jadi tidak ada nilai untuk saya.
Yggdrasil
1
Pertanyaannya adalah, nilai apa yang saya miliki dari unit test, ketika nilai itu tidak pecah? Mengapa repot-repot menulisnya, ketika saya selalu perlu menyesuaikannya dengan persyaratan baru? Satu-satunya nilai dari mereka yang saya lihat adalah untuk algoritma dan kelas-kelas lain dengan permutasi tinggi. Tetapi ini lebih kecil dari tes komponen dan penerimaan.
Yggdrasil