Jenis tes unit berdasarkan kegunaan


13

Dari sudut pandang nilai, saya melihat dua kelompok tes unit dalam praktik saya:

  1. Tes yang menguji beberapa logika non-sepele. Menulisnya (baik sebelum implementasi atau setelah) mengungkapkan beberapa masalah / bug potensial dan membantu untuk percaya diri jika logika diubah di masa depan.
  2. Tes yang menguji beberapa logika yang sangat sepele. Tes-tes itu lebih seperti kode dokumen (biasanya dengan mengejek) daripada mengujinya. Alur kerja pemeliharaan dari tes-tes itu bukan "beberapa logika berubah, tes menjadi merah - terima kasih Tuhan saya menulis tes ini" tetapi "beberapa kode sepele berubah, tes menjadi negatif palsu - saya harus mempertahankan (menulis ulang) tes tanpa mendapatkan keuntungan apa pun" . Sebagian besar waktu tes itu tidak layak dipertahankan (kecuali alasan agama). Dan menurut pengalaman saya di banyak sistem, tes-tes itu seperti 80% dari semua tes.

Saya mencoba mencari tahu apa pendapat orang lain tentang topik pemisahan unit tes berdasarkan nilai dan bagaimana hal itu sesuai dengan pemisahan saya. Tapi yang paling sering saya lihat adalah propaganda TDD penuh-waktu atau tes-tidak-berguna-hanya-menulis-kode-propaganda. Saya tertarik pada sesuatu di tengah. Pikiran Anda sendiri atau referensi untuk artikel / makalah / buku dipersilakan.


3
Saya terus menguji unit untuk menemukan bug (spesifik) yang diketahui - yang pernah lolos dari set unit test asli - sebagai kelompok terpisah yang perannya adalah untuk mencegah bug regresi.
Konrad Morawski

6
Kedua jenis tes itu adalah apa yang saya pandang sebagai semacam "perubahan gesekan". Jangan mengabaikan kegunaannya. Mengubah bahkan hal-hal sepele dari kode cenderung memiliki efek riak di seluruh basis kode, dan memperkenalkan jenis gesekan ini bertindak sebagai rintangan bagi pengembang Anda sehingga mereka hanya mengubah hal-hal yang benar - benar membutuhkannya, daripada berdasarkan pada preferensi pribadi atau preferensi.
Telastyn

3
@ Telastyn - Segala sesuatu tentang komentar Anda tampaknya sangat gila bagi saya. Siapa yang akan sengaja membuat sulit untuk mengubah kode? Mengapa mencegah pengembang untuk mengubah kode yang mereka inginkan - apakah Anda tidak mempercayai mereka? Apakah mereka pengembang yang buruk?
Benjamin Hodgson

2
Bagaimanapun, jika mengubah kode cenderung memiliki "efek riak" maka kode Anda memiliki masalah desain - dalam hal ini devs harus didorong untuk refactor sebanyak yang masuk akal. Tes rapuh secara aktif mencegah refactoring (tes gagal; siapa yang dapat diganggu untuk mengetahui apakah tes itu adalah salah satu dari 80% tes yang tidak benar-benar melakukan apa-apa? Anda hanya menemukan cara berbeda dan lebih rumit untuk melakukannya). Tetapi Anda tampaknya memandang ini sebagai karakteristik yang diinginkan ... Saya tidak mengerti sama sekali.
Benjamin Hodgson

2
Bagaimanapun, OP dapat menemukan posting blog ini dari pencipta Rails menjadi menarik. Untuk terlalu menyederhanakan poinnya, Anda mungkin harus mencoba untuk membuang 80% tes itu.
Benjamin Hodgson

Jawaban:


14

Saya pikir itu wajar untuk menemukan kesenjangan dalam pengujian unit. Ada banyak pendapat berbeda tentang cara melakukannya dengan benar dan secara alami semua pendapat lain pada dasarnya salah . Ada beberapa artikel di DrDobbs baru-baru ini yang mengeksplorasi masalah ini yang saya tautkan di akhir jawaban saya.

Masalah pertama yang saya lihat dengan tes adalah mudah salah. Di kelas C ++ perguruan tinggi saya, kami dihadapkan dengan tes unit di semester pertama dan kedua. Kami tidak tahu apa-apa tentang pemrograman secara umum di salah satu semester - kami mencoba mempelajari dasar-dasar pemrograman melalui C ++. Sekarang bayangkan katakan kepada siswa, "Oh, hei, Anda menulis kalkulator pajak tahunan! Sekarang tulis beberapa tes unit untuk memastikan bahwa itu berfungsi dengan benar." Hasilnya harus jelas - semuanya mengerikan, termasuk upaya saya.

Setelah Anda mengakui bahwa Anda payah dalam menulis unit test dan ingin menjadi lebih baik, Anda akan segera dihadapkan dengan gaya pengujian yang trendi atau metodologi yang berbeda. Dengan menguji metodologi saya merujuk pada praktik-praktik seperti tes pertama atau apa yang dilakukan Andrew Binstock dari DrDobbs, yaitu menulis tes bersama kode. Keduanya memiliki pro dan kontra dan saya menolak untuk masuk ke detail subjektif karena itu akan memicu perang api. Jika Anda tidak bingung tentang metodologi pemrograman mana yang lebih baik, maka mungkin gaya pengujian akan berhasil. Haruskah Anda menggunakan TDD, BDD, pengujian berbasis properti? JUnit memiliki konsep canggih yang disebut Teori yang mengaburkan garis antara TDD dan pengujian berbasis properti. Yang harus digunakan kapan?

tl; dr Sangat mudah untuk mendapatkan pengujian yang salah, sangat disarankan dan saya tidak percaya bahwa satu metodologi pengujian pun secara inheren lebih baik selama mereka digunakan dengan tekun dan profesional dalam konteks yang sesuai. Selanjutnya, pengujian adalah dalam benak saya, perluasan ke asersi atau tes kewarasan yang digunakan untuk memastikan pendekatan ad-hoc gagal-cepat untuk pembangunan yang sekarang jauh, lebih mudah.

Untuk pendapat subyektif, saya lebih suka menulis "fase" tes, karena tidak ada frasa yang lebih baik. Saya menulis tes unit yang menguji kelas secara terpisah, menggunakan tiruan di mana perlu. Ini mungkin akan dieksekusi dengan JUnit atau yang serupa. Lalu saya menulis tes integrasi atau penerimaan, ini dijalankan secara terpisah dan biasanya hanya beberapa kali sehari. Ini adalah kasus penggunaan non-sepele Anda. Saya biasanya menggunakan BDD karena bagus untuk mengekspresikan fitur dalam bahasa alami, sesuatu yang JUnit tidak dapat dengan mudah menyediakannya.

Terakhir, sumber daya. Ini akan menyajikan pendapat yang saling bertentangan sebagian besar berpusat di sekitar pengujian unit dalam berbagai bahasa dan dengan kerangka kerja yang berbeda. Mereka harus menyajikan perbedaan dalam ideologi dan metodologi sambil memungkinkan Anda untuk membuat pendapat Anda sendiri selama saya belum banyak memanipulasi pendapat Anda :)

[1] Korupsi Agile oleh Andrew Binstock

[2] Menanggapi Tanggapan dari artikel sebelumnya

[3] Tanggapan untuk Korupsi Agile oleh Paman Bob

[4] Tanggapan untuk Korupsi Agile oleh Rob Myers

[5] Kenapa repot dengan pengujian mentimun?

[6] Anda Membuatnya Salah

[7] Keluar dari Alat

[8] Komentar tentang 'Angka Romawi Kata dengan Komentar'

[9] Angka Romawi Kata Dengan Komentar


1
Salah satu pertengkaran ramah saya adalah bahwa jika Anda menulis tes untuk menguji fungsi kalkulator pajak tahunan, maka Anda tidak menulis tes unit. Itu tes integrasi. Kalkulator Anda harus dipecah menjadi unit eksekusi yang cukup sederhana, dan unit Anda menguji kemudian menguji unit tersebut. Jika salah satu unit berhenti berfungsi dengan benar (tes mulai gagal), maka itu seperti merobohkan bagian dari dinding fondasi, dan Anda perlu memperbaiki kode (bukan tes, umumnya). Entah itu, atau Anda telah mengidentifikasi sedikit kode yang tidak lagi diperlukan dan harus dibuang.
Craig

1
@Craig: Tepatnya! Inilah yang saya maksud dengan tidak tahu cara menulis tes yang tepat. Sebagai seorang mahasiswa, pemungut pajak adalah satu kelas besar yang ditulis tanpa pemahaman yang memadai tentang SOLID. Anda benar-benar berpikir bahwa ini lebih merupakan tes integrasi daripada yang lain, tapi itu istilah yang tidak diketahui bagi kami. Kami hanya dihadapkan pada tes "unit" oleh profesor kami.
IAE

5

Saya percaya penting untuk melakukan tes dari kedua jenis dan menggunakannya jika perlu.

Seperti yang Anda katakan, ada dua ekstrem dan saya jujur ​​tidak setuju dengan yang satu juga.

Kuncinya adalah bahwa unit test harus mencakup aturan dan persyaratan bisnis . Jika ada persyaratan bahwa sistem harus melacak usia seseorang, tulis tes "sepele" untuk memastikan usia adalah bilangan bulat negatif. Anda sedang menguji domain data yang diperlukan oleh sistem: sementara sepele, ia memiliki nilai karena menegakkan parameter sistem .

Begitu juga dengan tes yang lebih kompleks, mereka harus membawa nilai. Tentu, Anda dapat menulis tes yang memvalidasi sesuatu yang bukan persyaratan tetapi harus ditegakkan di menara gading di suatu tempat, tetapi itu adalah waktu yang lebih baik untuk menulis tes yang memvalidasi persyaratan yang pelanggan bayar Anda. Misalnya, mengapa menulis tes yang memvalidasi kode Anda dapat menangani aliran input yang keluar, ketika satu-satunya aliran berasal dari file lokal, bukan jaringan?

Saya sangat percaya pada pengujian unit dan menggunakan TDD dimanapun itu masuk akal. Unit test tentu saja membawa nilai dalam bentuk peningkatan kualitas dan perilaku "gagal cepat" ketika mengubah kode. Namun, ada aturan lama 80/20 yang perlu diingat juga. Pada titik tertentu Anda akan mencapai hasil yang semakin berkurang saat menulis tes, dan Anda harus beralih ke pekerjaan yang lebih produktif bahkan jika ada beberapa nilai terukur yang bisa didapat dari menulis lebih banyak tes.


Menulis tes untuk memastikan bahwa sistem melacak usia seseorang bukan tes unit, IMO. Itu tes integrasi. Tes unit akan menguji unit eksekusi umum (alias "prosedur") yang, katakanlah, menghitung nilai usia dari, katakanlah, tanggal dasar dan offset dalam unit apa pun (hari, minggu, dll). Maksud saya adalah sedikit kode seharusnya tidak memiliki dependensi keluar aneh pada sisa sistem. Ini HANYA menghitung usia dari beberapa nilai input, dan dalam hal ini uji unit dapat mengkonfirmasi perilaku yang benar, yang mungkin untuk membuang pengecualian jika offset menghasilkan usia negatif.
Craig

Saya tidak mengacu pada perhitungan apa pun. Jika model menyimpan sepotong data, itu dapat memvalidasi data milik domain yang benar. Dalam hal ini, domain adalah himpunan bilangan bulat negatif. Perhitungan harus terjadi di controller (dalam MVC), dan dalam contoh ini perhitungan umur akan menjadi tes terpisah.

4

Inilah pendapat saya: semua tes memiliki biaya:

  • waktu dan usaha awal:
    • pikirkan apa yang harus diuji dan bagaimana mengujinya
    • mengimplementasikan tes dan memastikan itu menguji apa yang seharusnya
  • pemeliharaan berkelanjutan
    • memastikan tes masih melakukan apa yang seharusnya dilakukan ketika kode berevolusi secara alami
  • menjalankan tes
    • waktu eksekusi
    • menganalisis hasilnya

Kami juga bermaksud agar semua tes memberikan manfaat (dan menurut pengalaman saya, hampir semua tes memberikan manfaat):

  • spesifikasi
  • sorot kasus sudut
  • mencegah regresi
  • verifikasi otomatis
  • contoh penggunaan API
  • kuantifikasi sifat spesifik (waktu, ruang)

Jadi cukup mudah untuk melihat bahwa jika Anda menulis banyak tes, mereka mungkin akan memiliki nilai. Di mana ini menjadi rumit adalah ketika Anda mulai membandingkan nilai itu (yang, omong-omong, Anda mungkin tidak tahu sebelumnya - jika Anda membuang kode Anda, tes regresi kehilangan nilainya) dengan biaya.

Sekarang, waktu dan usaha Anda terbatas. Anda ingin memilih untuk melakukan hal-hal yang memberikan manfaat paling banyak dengan biaya paling sedikit. Dan saya pikir itu adalah hal yang sangat sulit untuk dilakukan, paling tidak karena itu mungkin memerlukan pengetahuan bahwa seseorang tidak memiliki atau akan mahal untuk diperoleh.

Dan itulah intisari nyata antara berbagai pendekatan yang berbeda ini. Saya percaya mereka semua telah mengidentifikasi strategi pengujian yang bermanfaat. Namun, setiap strategi memiliki biaya dan manfaat yang berbeda secara umum. Juga, biaya dan manfaat dari masing-masing strategi mungkin akan sangat tergantung pada spesifikasi proyek, domain, dan tim. Dengan kata lain, mungkin ada beberapa jawaban terbaik.

Dalam beberapa kasus, memompa kode tanpa tes dapat memberikan manfaat / biaya terbaik. Dalam kasus lain, test suite menyeluruh mungkin lebih baik. Dalam kasus lain, memperbaiki desain mungkin merupakan hal terbaik untuk dilakukan.


2

Apa adalah sebuah satuan tes, benar-benar? Dan apakah benar-benar ada dikotomi besar yang dimainkan di sini?

Kami bekerja di bidang di mana membaca secara harfiah satu bit melewati ujung buffer benar-benar dapat crash program, atau menyebabkannya menghasilkan hasil yang benar-benar tidak akurat, atau sebagaimana dibuktikan oleh bug TLS "HeartBleed" baru-baru ini, meletakkan sistem yang seharusnya aman terbuka tanpa menghasilkan bukti langsung adanya cacat.

Tidak mungkin untuk menghilangkan semua kompleksitas dari sistem ini. Tetapi tugas kami adalah, sejauh mungkin, untuk meminimalkan dan mengelola kompleksitas itu.

Apakah unit menguji tes yang mengonfirmasi, misalnya, bahwa reservasi berhasil diposting dalam tiga sistem yang berbeda, entri log dibuat dan konfirmasi Email dikirim?

Saya akan mengatakan tidak . Itu sebuah integrasi tes . Dan yang paling pasti memiliki tempat mereka, tetapi mereka juga topik yang berbeda.

Tes integrasi berfungsi untuk mengonfirmasi fungsi keseluruhan dari seluruh "fitur". Tetapi kode di balik fitur itu harus dipecah menjadi blok bangunan sederhana yang dapat diuji, alias "unit."

Jadi tes unit harus memiliki ruang lingkup yang sangat terbatas.

Yang menyiratkan bahwa kode yang diuji oleh unit test harus memiliki cakupan yang sangat terbatas.

Yang lebih jauh menyiratkan bahwa salah satu pilar desain yang baik adalah untuk memecah masalah kompleks Anda menjadi bagian-bagian yang lebih kecil, satu-tujuan (sejauh mungkin) yang dapat diuji dalam isolasi relatif satu sama lain.

Yang akhirnya Anda dapatkan adalah sebuah sistem yang terbuat dari komponen dasar yang andal, dan Anda tahu jika ada salah satu unit dasar kode tersebut rusak karena Anda telah menulis tes sederhana, kecil, dengan cakupan terbatas untuk memberi tahu Anda dengan tepat.

Dalam banyak kasus Anda mungkin juga harus melakukan beberapa tes per unit. Tes itu sendiri harus sederhana, menguji satu dan hanya satu perilaku sejauh mungkin.

Gagasan tentang "unit test" yang menguji logika yang non-sepele, rumit, rumit, saya pikir, sedikit dari sebuah oxymoron.

Jadi, jika kerusakan desain yang disengaja semacam itu terjadi, lalu bagaimana mungkin sebuah unit test tiba-tiba mulai menghasilkan false positive, kecuali fungsi dasar unit kode yang diuji telah berubah? Dan jika itu terjadi, lebih baik Anda percaya ada beberapa efek riak yang tidak jelas dalam permainan. Tes rusak Anda, salah satu yang tampaknya menghasilkan positif palsu, sebenarnya memperingatkan Anda bahwa beberapa perubahan telah merusak lingkaran dependensi yang lebih luas dalam basis kode, dan itu perlu diperiksa dan diperbaiki.

Beberapa unit tersebut (banyak di antaranya) mungkin perlu diuji dengan menggunakan objek tiruan, tetapi itu tidak berarti Anda harus menulis tes yang lebih rumit atau rumit.

Kembali ke contoh buat saya dari sistem reservasi, Anda benar-benar tidak dapat mengirimkan permintaan ke sebuah basis data reservasi hidup atau layanan pihak ketiga (atau bahkan "dev" instance dari itu) setiap kali Anda satuan pengujian kode Anda.

Jadi Anda menggunakan tiruan yang menghadirkan kontrak antarmuka yang sama. Pengujian kemudian dapat memvalidasi perilaku sepotong kode yang relatif kecil dan deterministik. Hijau semua di papan kemudian memberitahu Anda bahwa blok yang membentuk fondasi Anda tidak rusak.

Tetapi logika unit individu menguji diri mereka sendiri tetap sesederhana mungkin.


1

Ini tentu saja, hanya pendapat saya, tetapi setelah menghabiskan beberapa bulan terakhir belajar pemrograman fungsional dalam fsharp (berasal dari latar belakang C #) telah membuat saya menyadari beberapa hal.

Seperti yang dinyatakan OP, biasanya ada 2 jenis "tes unit" yang kita lihat sehari-hari. Pengujian yang mencakup masuk dan keluarnya suatu metode, yang umumnya paling berharga, tetapi sulit dilakukan untuk 80% dari sistem yang kurang tentang "algoritma" dan lebih banyak tentang "abstraksi".

Jenis lainnya, menguji interaktivitas abstraksi, biasanya melibatkan mengejek. Menurut pendapat saya, sebagian besar tes ini diperlukan karena desain aplikasi Anda. Mengatasinya, dan Anda berisiko bug aneh dan kode spagetti, karena orang tidak berpikir tentang desain mereka dengan benar kecuali mereka dipaksa untuk melakukan tes terlebih dahulu (dan bahkan kemudian, biasanya mengacaukannya). Masalahnya bukan metodologi pengujian, tetapi desain yang mendasari sistem. Sebagian besar sistem yang dibangun dengan bahasa imperatif atau OO memiliki ketergantungan yang melekat pada "efek samping" alias "Lakukan ini, tapi jangan bilang apa-apa padaku". Ketika Anda mengandalkan efek samping, Anda perlu mengujinya, karena persyaratan bisnis atau operasi biasanya merupakan bagian dari itu.

Ketika Anda merancang sistem Anda dengan cara yang lebih fungsional, di mana Anda menghindari membangun ketergantungan pada efek samping dan menghindari perubahan keadaan / pelacakan melalui ketidakberdayaan, ini memungkinkan Anda untuk lebih fokus pada tes "seluk beluk", yang jelas menguji lebih banyak aksi , dan sedikit bagaimana Anda sampai di sana. Anda akan kagum dengan hal-hal seperti immutability yang dapat memberi Anda solusi yang jauh lebih sederhana untuk masalah yang sama, dan ketika Anda tidak lagi bergantung pada "efek samping" Anda dapat melakukan hal-hal seperti pemrograman paralel dan asinkron dengan hampir tanpa biaya tambahan.

Sejak saya mulai menulis kode di Fsharp, saya tidak memerlukan kerangka mengejek untuk apa pun, dan bahkan telah melepaskan ketergantungan saya sama sekali pada IOC Container. Tes saya didorong oleh kebutuhan dan nilai bisnis, dan bukan pada lapisan abstraksi berat yang biasanya diperlukan untuk mencapai komposisi dalam pemrograman imperatif.

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.