Dalam komentar untuk posting hebat ini , Roy Osherove menyebutkan proyek OAPT yang dirancang untuk menjalankan setiap pernyataan dalam satu tes.
Berikut ini ditulis di halaman muka proyek:
Tes unit yang tepat harus gagal karena satu alasan, karena itu Anda harus menggunakan satu pengujian per unit.
Dan, juga, Roy menulis dalam komentar:
Pedoman saya biasanya bahwa Anda menguji satu KONSEP logis per tes. Anda dapat memiliki banyak penegasan tentang objek yang sama . mereka biasanya akan menjadi konsep yang sama sedang diuji.
Saya pikir, ada beberapa kasus di mana diperlukan beberapa pernyataan (mis. Penjagaan Pernyataan ), tetapi secara umum saya mencoba menghindari ini. Apa pendapat Anda? Tolong berikan contoh dunia nyata di mana beberapa pernyataan sangat dibutuhkan .
sumber
RowTest
(MbUnit) /TestCase
(NUnit) untuk menguji berbagai perilaku kasus tepi. Gunakan alat yang tepat untuk pekerjaan itu! (Sayangnya, MSTest tampaknya belum memiliki kemampuan uji baris.)RowTest
danTestCase
menggunakan sumber data uji . Saya menggunakan file CSV sederhana dengan kesuksesan besar.Jawaban:
Saya tidak berpikir itu hal yang buruk , tetapi saya pikir kita harus berusaha keras untuk hanya memiliki satu menegaskan dalam tes kami. Ini berarti Anda menulis lebih banyak tes dan pengujian kami hanya akan menguji satu per satu.
Karena itu, saya akan mengatakan mungkin setengah dari tes saya sebenarnya hanya memiliki satu pernyataan. Saya pikir itu hanya menjadi bau kode (tes?) Ketika Anda memiliki sekitar lima atau lebih menegaskan dalam tes Anda.
Bagaimana Anda memecahkan banyak pernyataan?
sumber
Tes harus gagal hanya karena satu alasan, tetapi itu tidak selalu berarti bahwa hanya ada satu
Assert
pernyataan. IMHO, lebih penting memegang pola " Atur, Tindak, Tegas ".Kuncinya adalah bahwa Anda hanya memiliki satu tindakan, dan kemudian Anda memeriksa hasil dari tindakan itu menggunakan pernyataan. Tapi itu adalah "Atur, Bertindak, Tegas, Akhir dari ujian ". Jika Anda tergoda untuk melanjutkan pengujian dengan melakukan tindakan lain dan lebih banyak konfirmasi sesudahnya, lakukan itu sebagai tes terpisah.
Saya senang melihat beberapa pernyataan tegas yang merupakan bagian dari pengujian tindakan yang sama. misalnya
atau
Anda dapat menggabungkan ini menjadi satu pernyataan, tetapi itu hal yang berbeda dari bersikeras bahwa Anda harus atau harus . Tidak ada peningkatan dari menggabungkan mereka.
misalnya Yang pertama bisa menjadi
Tapi ini tidak lebih baik - pesan kesalahan dari itu kurang spesifik, dan tidak memiliki kelebihan lain. Saya yakin Anda dapat memikirkan contoh-contoh lain di mana menggabungkan dua atau tiga (atau lebih) menegaskan menjadi satu kondisi boolean besar membuatnya lebih sulit untuk dibaca, lebih sulit untuk mengubah dan lebih sulit untuk mengetahui mengapa itu gagal. Mengapa ini hanya demi aturan?
NB : Kode yang saya tulis di sini adalah C # dengan NUnit, tetapi prinsip-prinsipnya akan berlaku dengan bahasa dan kerangka kerja lain. Sintaksnya mungkin sangat mirip juga.
sumber
Assert.IsBetween(10, 100, value)
yang mencetakExpected 8 to be between 10 and 100
adalah lebih baik dari dua terpisah menegaskan dalam pendapat saya. Anda tentu bisa berpendapat bahwa itu tidak perlu, tetapi biasanya patut dipertimbangkan apakah mudah untuk mengurangi menjadi satu pernyataan sebelum membuat seluruh rangkaian dari mereka.Saya tidak pernah berpikir bahwa lebih dari satu pernyataan adalah hal yang buruk.
Saya selalu melakukannya:
Di sini saya menggunakan beberapa pernyataan untuk memastikan kondisi rumit dapat diubah menjadi predikat yang diharapkan.
Saya hanya menguji satu unit (
ToPredicate
metode), tetapi saya mencakup semua yang dapat saya pikirkan dalam pengujian.sumber
Ketika saya menggunakan pengujian unit untuk memvalidasi perilaku tingkat tinggi, saya benar-benar memasukkan beberapa pernyataan dalam satu tes. Inilah tes yang sebenarnya saya gunakan untuk beberapa kode pemberitahuan darurat. Kode yang berjalan sebelum pengujian menempatkan sistem ke dalam kondisi di mana jika prosesor utama dijalankan, alarm akan dikirim.
Ini mewakili kondisi yang perlu ada pada setiap langkah dalam proses agar saya yakin bahwa kode berperilaku seperti yang saya harapkan. Jika satu pernyataan gagal, saya tidak peduli bahwa yang tersisa bahkan tidak akan dijalankan; karena keadaan sistem tidak lagi valid, pernyataan berikutnya tidak akan memberi tahu saya sesuatu yang berharga. * Jika
assertAllUnitsAlerting()
gagal, maka saya tidak akan tahu apa yang membuatassertAllNotificationSent()
kesuksesan ATAU kegagalan sampai saya menentukan apa yang menyebabkan kesalahan sebelumnya dan memperbaikinya.(* - Oke, mereka mungkin berguna dalam men-debug masalah. Tetapi informasi yang paling penting, bahwa tes gagal, telah diterima.)
sumber
Alasan lain mengapa saya berpikir, bahwa banyak menegaskan dalam satu metode bukanlah hal yang buruk dijelaskan dalam kode berikut:
Dalam pengujian saya, saya hanya ingin menguji yang
service.process()
mengembalikan nomor yang benar dalamInner
instance kelas.Alih-alih menguji ...
Aku melakukan
sumber
Assert.notNull
S Anda berlebihan, pengujian Anda akan gagal dengan NPE jika batal.if
) akan lulus jikares
adalahnull
Assert.assertEquals(..., service.process().getInner());
, mungkin dengan variabel yang diekstraksi jika garisnya menjadi "terlalu panjang"Saya pikir ada banyak kasus di mana menulis beberapa pernyataan valid dalam aturan bahwa tes hanya boleh gagal karena satu alasan.
Misalnya, bayangkan fungsi yang mem-parsing string tanggal:
Jika tes gagal itu karena satu alasan, penguraian salah. Jika Anda berpendapat bahwa tes ini dapat gagal karena tiga alasan berbeda, Anda akan terlalu baik dalam definisi "satu alasan".
sumber
Saya tidak tahu situasi apa pun di mana itu adalah ide yang baik untuk memiliki beberapa pernyataan di dalam metode [Test] itu sendiri. Alasan utama orang-orang suka memiliki beberapa Pernyataan adalah mereka mencoba untuk memiliki satu kelas [TestFixture] untuk setiap kelas yang diuji. Sebagai gantinya, Anda dapat memecah tes Anda menjadi lebih banyak kelas [TestFixture]. Ini memungkinkan Anda melihat banyak cara di mana kode tersebut mungkin tidak bereaksi seperti yang Anda harapkan, alih-alih hanya di mana pernyataan pertama gagal. Cara Anda mencapainya adalah Anda memiliki setidaknya satu direktori per kelas yang sedang diuji dengan banyak kelas [TestFixture] di dalamnya. Setiap kelas [TestFixture] akan dinamai sesuai dengan keadaan spesifik objek yang akan Anda uji. Metode [SetUp] akan membuat objek ke keadaan yang dijelaskan oleh nama kelas. Kemudian Anda memiliki beberapa metode [Uji] yang masing-masing menyatakan hal-hal yang berbeda yang Anda harapkan benar, mengingat keadaan objek saat ini. Setiap metode [Tes] dinamai sesuai dengan apa yang ditegaskannya, kecuali mungkin itu dinamai berdasarkan konsep alih-alih hanya pembacaan bahasa Inggris dari kode. Maka setiap implementasi metode [Test] hanya membutuhkan satu baris kode di mana ia menyatakan sesuatu. Keuntungan lain dari pendekatan ini adalah membuat tes sangat mudah dibaca karena menjadi sangat jelas apa yang Anda uji, dan apa yang Anda harapkan hanya dengan melihat kelas dan nama metode. Ini juga akan skala lebih baik ketika Anda mulai menyadari semua kasus tepi kecil yang ingin Anda uji dan ketika Anda menemukan bug. kecuali mungkin itu dinamai setelah konsep bukan hanya pembacaan kode bahasa Inggris. Maka setiap implementasi metode [Test] hanya membutuhkan satu baris kode di mana ia menyatakan sesuatu. Keuntungan lain dari pendekatan ini adalah membuat tes sangat mudah dibaca karena menjadi sangat jelas apa yang Anda uji, dan apa yang Anda harapkan hanya dengan melihat kelas dan nama metode. Ini juga akan skala lebih baik ketika Anda mulai menyadari semua kasus tepi kecil yang ingin Anda uji dan ketika Anda menemukan bug. kecuali mungkin itu dinamai setelah konsep bukan hanya pembacaan kode bahasa Inggris. Maka setiap implementasi metode [Test] hanya membutuhkan satu baris kode di mana ia menyatakan sesuatu. Keuntungan lain dari pendekatan ini adalah membuat tes sangat mudah dibaca karena menjadi sangat jelas apa yang Anda uji, dan apa yang Anda harapkan hanya dengan melihat kelas dan nama metode. Ini juga akan skala lebih baik ketika Anda mulai menyadari semua kasus tepi kecil yang ingin Anda uji dan ketika Anda menemukan bug. dan apa yang Anda harapkan hanya dengan melihat kelas dan nama metode. Ini juga akan skala lebih baik ketika Anda mulai menyadari semua kasus tepi kecil yang ingin Anda uji dan ketika Anda menemukan bug. dan apa yang Anda harapkan hanya dengan melihat kelas dan nama metode. Ini juga akan skala lebih baik ketika Anda mulai menyadari semua kasus tepi kecil yang ingin Anda uji dan ketika Anda menemukan bug.
Biasanya ini berarti bahwa baris terakhir kode di dalam metode [SetUp] harus menyimpan nilai properti atau nilai pengembalian dalam variabel instance pribadi dari [TestFixture]. Maka Anda mungkin menegaskan beberapa hal berbeda tentang variabel instance ini dari metode [Tes] yang berbeda. Anda juga dapat membuat pernyataan tentang apa properti yang berbeda dari objek yang diuji diatur sekarang dalam keadaan yang diinginkan.
Kadang-kadang Anda perlu membuat pernyataan di sepanjang jalan saat Anda mendapatkan objek yang diuji dalam keadaan yang diinginkan untuk memastikan Anda tidak mengacaukan sebelum memasukkan objek ke dalam keadaan yang diinginkan. Dalam hal itu, pernyataan ekstra tersebut akan muncul di dalam metode [SetUp]. Jika ada yang salah di dalam metode [SetUp], akan menjadi jelas bahwa ada sesuatu yang salah dengan pengujian sebelum objek masuk ke kondisi yang diinginkan yang ingin Anda uji.
Satu masalah lain yang mungkin Anda temui adalah Anda mungkin sedang menguji Pengecualian yang Anda duga akan dibuang. Ini bisa menggoda Anda untuk tidak mengikuti model di atas. Namun, itu masih bisa dicapai dengan menangkap pengecualian di dalam metode [SetUp] dan menyimpannya ke dalam variabel instan. Ini akan memungkinkan Anda untuk menegaskan hal-hal yang berbeda tentang pengecualian, masing-masing dengan metode [Tes] sendiri. Anda kemudian dapat juga menegaskan hal-hal lain tentang objek yang sedang diuji untuk memastikan tidak ada efek samping yang tidak diinginkan dari pengecualian yang dilemparkan.
Contoh (ini akan dipecah menjadi beberapa file):
sumber
[Test]
metode, kelas itu instantiated dan[SetUp]
metode dieksekusi lagi. Ini membunuh .NET Garbage Collector dan menyebabkan pengujian berjalan sangat lambat: 5+ menit secara lokal, 20+ menit di server build. Tes 20K akan berjalan dalam 2 - 3 menit. Saya tidak akan merekomendasikan gaya pengujian ini sama sekali, terutama untuk test suite besar.Memiliki beberapa pernyataan dalam tes yang sama hanya merupakan masalah ketika tes gagal. Maka Anda mungkin harus men-debug tes atau menganalisis pengecualian untuk mengetahui pernyataan mana yang gagal. Dengan satu pernyataan di setiap tes, biasanya lebih mudah untuk menentukan apa yang salah.
Saya tidak bisa memikirkan skenario di mana pernyataan banyak benar-benar diperlukan , karena Anda selalu dapat menulis ulang mereka sebagai beberapa kondisi dalam pernyataan yang sama. Namun mungkin lebih disukai jika Anda misalnya memiliki beberapa langkah untuk memverifikasi data perantara antara langkah-langkah daripada mengambil risiko bahwa langkah-langkah selanjutnya macet karena input yang buruk.
sumber
Jika tes Anda gagal, Anda tidak akan tahu apakah pernyataan berikut juga akan rusak. Seringkali, itu berarti Anda akan kehilangan informasi berharga untuk mencari tahu sumber masalahnya. Solusi saya adalah menggunakan satu pernyataan tetapi dengan beberapa nilai:
Itu memungkinkan saya untuk melihat semua pernyataan yang gagal sekaligus. Saya menggunakan beberapa baris karena kebanyakan IDE akan menampilkan perbedaan string dalam dialog perbandingan berdampingan.
sumber
Jika Anda memiliki beberapa pernyataan dalam satu fungsi tes, saya berharap mereka secara langsung relevan dengan tes yang Anda lakukan. Sebagai contoh,
Memiliki banyak tes (bahkan ketika Anda merasa itu mungkin berlebihan) bukanlah hal yang buruk. Anda dapat berargumen bahwa memiliki tes vital dan paling esensial adalah lebih penting. JADI, ketika Anda menegaskan, pastikan pernyataan tegas Anda ditempatkan dengan benar daripada terlalu khawatir tentang banyak pernyataan. Jika Anda membutuhkan lebih dari satu, gunakan lebih dari satu.
sumber
Tujuan dari unit test adalah untuk memberi Anda sebanyak mungkin informasi tentang apa yang gagal tetapi juga untuk membantu menentukan secara akurat masalah yang paling mendasar terlebih dahulu. Ketika Anda tahu secara logis bahwa satu pernyataan akan gagal mengingat bahwa pernyataan lain gagal atau dengan kata lain ada hubungan ketergantungan antara tes maka masuk akal untuk menggulirkan ini sebagai beberapa pernyataan dalam satu tes. Ini bermanfaat untuk tidak membuang sampah sembarangan hasil tes dengan kegagalan yang jelas yang bisa dihilangkan jika kita menebus pernyataan pertama dalam satu pengujian. Dalam kasus di mana hubungan ini tidak ada preferensi akan secara alami kemudian untuk memisahkan pernyataan ini menjadi tes individu karena jika menemukan kegagalan ini akan memerlukan beberapa iterasi dari uji coba untuk menyelesaikan semua masalah.
Jika kemudian Anda juga mendesain unit / kelas sedemikian rupa sehingga tes yang terlalu rumit perlu ditulis, itu membuat lebih sedikit beban selama pengujian dan mungkin mempromosikan desain yang lebih baik.
sumber
Ya, boleh saja memiliki beberapa pernyataan selama tes yang gagal memberi Anda cukup informasi untuk dapat mendiagnosis kegagalan tersebut. Ini akan tergantung pada apa yang Anda uji dan apa mode kegagalannya.
Saya tidak pernah menemukan formulasi seperti itu untuk membantu (bahwa kelas harus memiliki satu alasan untuk berubah adalah contoh dari pepatah yang tidak membantu seperti itu). Pertimbangkan pernyataan bahwa dua string sama, ini secara semantik setara dengan menyatakan bahwa panjang kedua string adalah sama dan setiap karakter pada indeks yang sesuai sama.
Kita dapat menggeneralisasi dan mengatakan bahwa sistem pernyataan ganda mana saja dapat ditulis ulang sebagai pernyataan tunggal, dan pernyataan tunggal mana pun dapat diuraikan menjadi seperangkat pernyataan yang lebih kecil.
Jadi, fokus saja pada kejelasan kode dan kejelasan hasil tes, dan biarkan itu memandu jumlah pernyataan yang Anda gunakan daripada sebaliknya.
sumber
Jawabannya sangat sederhana - jika Anda menguji fungsi yang mengubah lebih dari satu atribut, dari objek yang sama, atau bahkan dua objek yang berbeda, dan kebenaran fungsi tergantung pada hasil dari semua perubahan itu, maka Anda ingin menegaskan bahwa setiap perubahan itu telah dilakukan dengan benar!
Saya mendapatkan ide konsep yang logis, tetapi kesimpulan sebaliknya akan mengatakan bahwa tidak ada fungsi yang harus berubah lebih dari satu objek. Tapi itu tidak mungkin untuk diterapkan dalam semua kasus, dalam pengalaman saya.
Ambil konsep logis dari transaksi bank - menarik jumlah dari satu rekening bank dalam banyak kasus HARUS menyertakan penambahan jumlah itu ke akun lain. Anda TIDAK PERNAH ingin memisahkan kedua hal itu, mereka membentuk unit atom. Anda mungkin ingin membuat dua fungsi (withdraw / addMoney) dan karenanya menulis dua tes unit yang berbeda - sebagai tambahan. Tetapi kedua tindakan tersebut harus dilakukan dalam satu transaksi dan Anda juga ingin memastikan bahwa transaksi tersebut berhasil. Dalam hal ini tidak cukup untuk memastikan langkah-langkah individual berhasil. Anda harus memeriksa kedua rekening bank, dalam pengujian Anda.
Mungkin ada contoh yang lebih rumit yang Anda tidak akan uji dalam tes unit, di tempat pertama, tetapi dalam tes integrasi atau penerimaan. Tapi batas-batas itu lancar, IMHO! Tidak mudah untuk memutuskan, ini masalah keadaan dan mungkin preferensi pribadi. Menarik uang dari satu dan menambahkannya ke akun lain masih merupakan fungsi yang sangat sederhana dan jelas merupakan kandidat untuk pengujian unit.
sumber
Pertanyaan ini terkait dengan masalah klasik keseimbangan antara masalah kode spaghetti dan lasagna.
Memiliki beberapa pernyataan dapat dengan mudah masuk ke masalah spageti di mana Anda tidak memiliki gagasan tentang tes apa, tetapi memiliki satu pernyataan per tes dapat membuat pengujian Anda sama-sama tidak terbaca memiliki beberapa tes dalam lasagna besar membuat temuan tes mana yang melakukan apa yang tidak mungkin .
Ada beberapa pengecualian, tetapi dalam hal ini menjaga pendulum di tengah adalah jawabannya.
sumber
Saya bahkan tidak setuju dengan "gagal hanya karena satu alasan" secara umum. Yang lebih penting adalah bahwa tes itu singkat dan membaca jelas imo.
Hal ini tidak selalu dapat dicapai dan ketika pengujian menjadi rumit, nama deskriptif (panjang) dan pengujian lebih sedikit akan lebih masuk akal.
sumber