Pemrograman Berbasis Kontrak vs Tes Unit

13

Saya seorang programmer yang agak defensif dan penggemar berat Kontrak Kode Microsoft.

Sekarang saya tidak bisa selalu menggunakan C # dan dalam kebanyakan bahasa satu-satunya alat yang saya miliki adalah pernyataan. Jadi saya biasanya berakhir dengan kode seperti ini:

class
{       
    function()
    {   
         checkInvariants();
         assert(/* requirement */);

         try
         {
             /* implementation */
         }
         catch(...)
         {
              assert(/* exceptional ensures */);                  
         }
         finally
         {
              assert(/* ensures */);
              checkInvariants();
         }
    }

    void checkInvariants()
    {
         assert(/* invariant */);
    }
}

Namun, paradigma ini (atau apa pun namanya) menyebabkan banyak kode berantakan.

Saya sudah mulai bertanya-tanya apakah itu benar-benar sepadan dengan usaha dan apakah unit test yang tepat sudah akan membahas ini?

ronag
sumber
6
Sementara unit test memungkinkan untuk mengeluarkan pernyataan dari kode aplikasi (dan karena itu menghindari kekacauan), pertimbangkan bahwa mereka tidak dapat memeriksa semua yang dapat terjadi dalam sistem produksi nyata. Jadi kontrak kode IMO memiliki beberapa keunggulan, terutama untuk kode kritis di mana kebenaran sangat penting.
Giorgio
Jadi pada dasarnya waktu pengembangan, pemeliharaan, keterbacaan vs cakupan kode yang lebih baik?
ronag
1
Saya kebanyakan menggunakan pernyataan dalam kode untuk memeriksa kebenaran parameter (pada nol misalnya) dan dalam unit test juga memeriksa pernyataan tersebut.
artjom

Jawaban:

14

Saya tidak berpikir Anda harus menganggapnya sebagai "vs".
Seperti disebutkan dalam komentar oleh @Giorgio, kontrak kode adalah untuk memeriksa invarian (dalam lingkungan produksi) dan unit test memastikan bahwa kode Anda berfungsi seperti yang diharapkan ketika persyaratan tersebut dipenuhi.

duros
sumber
2
Saya pikir ini juga penting untuk menguji apakah kode itu berfungsi (misalnya melempar pengecualian) ketika kondisi tidak terpenuhi.
svick
6

Kontrak membantu Anda dengan setidaknya satu hal yang tidak dilakukan unit test. Saat Anda mengembangkan API publik, Anda tidak dapat menguji bagaimana orang menggunakan kode Anda. Namun Anda masih dapat menentukan kontrak untuk metode Anda.

Saya pribadi akan seketat ini tentang kontrak hanya ketika berhadapan dengan API modul publik. Dalam banyak kasus lain, ini mungkin tidak sepadan dengan usaha (dan Anda dapat menggunakan tes unit sebagai gantinya), tetapi ini hanya pendapat saya.

Itu tidak berarti saya menyarankan untuk tidak memikirkan kontrak dalam kasus-kasus itu. Saya selalu memikirkan mereka. Saya hanya tidak berpikir perlu untuk selalu secara eksplisit kode mereka.

Honza Brabec
sumber
1

Seperti telah disebutkan Kontrak dan unit test memiliki tujuan berbeda.

Kontrak adalah tentang pemrograman defensif untuk memastikan bahwa prasyarat terpenuhi, kode dipanggil dengan parameter yang benar, dll.

Tes unit untuk memastikan kode itu berfungsi, dalam skenario yang berbeda. Ini seperti 'spesifikasi dengan gigi'.

Asserts bagus mereka membuat kode kuat. Namun, jika Anda khawatir akan menambah banyak kode, Anda mungkin juga ingin menambahkan breakpoint bersyarat di beberapa tempat selama proses debug dan mengurangi jumlah Assert.

Sajad Deyargaroo
sumber
0

Apa pun yang Anda miliki di checkVariants () panggilan Anda dapat dilakukan dari pengujian, seberapa banyak upaya yang sebenarnya mungkin tergantung banyak hal (dependensi eksternal, tingkat penggandaan, dll.) Tetapi itu akan membersihkan kode dari satu sudut pandang. Bagaimana testable berbasis kode dikembangkan terhadap asersi tanpa refactoring saya tidak yakin.

Saya setuju dengan @duros, ini tidak boleh dianggap sebagai pendekatan yang eksklusif atau bersaing. Bahkan dalam lingkungan TDD Anda bahkan bisa berdebat bahwa pernyataan 'persyaratan' akan perlu memiliki tes :).

Akan tetapi, JANGAN membuat kode lebih kuat kecuali Anda benar-benar melakukan sesuatu untuk memperbaiki cek yang gagal, mereka hanya menghentikan data yang rusak atau serupa, biasanya dengan membatalkan pemrosesan pada tanda pertama masalah.

Suatu solusi yang digerakkan oleh tes / teruji dengan baik biasanya sudah memikirkan dan / atau menemukan banyak sumber / alasan input dan output yang buruk sementara mengembangkan komponen yang berinteraksi dan sudah berurusan dengan mereka lebih dekat ke sumber masalah.

Jika sumber Anda adalah eksternal dan Anda tidak memiliki kontrol atasnya maka untuk menghindari mengacaukan kode Anda berurusan dengan masalah kode lain, Anda mungkin mempertimbangkan untuk menerapkan semacam komponen pembersihan-data / pernyataan di antara sumber dan komponen Anda dan meletakkan cek Anda di sana .

Saya juga ingin tahu bahasa apa yang Anda gunakan yang tidak memiliki semacam xUnit atau pustaka pengujian lain yang telah dikembangkan seseorang, saya pikir ada cukup banyak sesuatu untuk semuanya akhir-akhir ini?

Chris Lee
sumber
0

Selain pengujian unit dan kontrak kode, saya hanya berpikir saya akan menyoroti aspek lain, yang merupakan nilai dalam mendefinisikan antarmuka Anda sehingga Anda menghilangkan atau mengurangi kemungkinan memanggil kode Anda secara salah.

Ini tidak selalu mudah atau mungkin, tetapi pasti patut bertanya pada diri sendiri pertanyaan, "Bisakah saya membuat kode ini lebih mudah?"

Anders Hejlsberg, pencipta C #, mengatakan bahwa salah satu kesalahan terbesar dalam C # tidak termasuk jenis referensi yang tidak dapat dibatalkan. Itu salah satu alasan utama begitu banyak kekacauan kode penjaga diperlukan.

Namun, refactoring untuk hanya memiliki jumlah kode penjaga yang diperlukan dan memadai membuat, menurut pengalaman saya, lebih mudah digunakan dan dipelihara kode.

Setuju dengan @duros untuk sisanya.

James World
sumber
0

Lakukan keduanya, tetapi buat beberapa metode pembantu statis untuk mengklarifikasi niat Anda. Inilah yang dilakukan Google untuk Java, periksa code.google.com/p/guava-libraries/wiki/PreconditionsExplained

Alexander Torstling
sumber