Unit menguji kode fungsional yang diketik secara statis

15

Saya ingin bertanya kepada kalian, dalam hal ini masuk akal untuk menguji unit kode fungsional yang diketik secara statis, seperti yang ditulis dalam haskell, scala, ocaml, nemerle, f # atau haXe (yang terakhir adalah apa yang saya benar-benar tertarik, tetapi saya ingin memanfaatkan pengetahuan komunitas yang lebih besar).

Saya menanyakan ini karena dari pengertian saya:

  • Salah satu aspek dari unit test adalah memiliki spesifikasi dalam bentuk runnable. Namun ketika menggunakan gaya deklaratif, yang secara langsung memetakan spesifikasi yang diformalkan ke semantik bahasa, apakah bahkan mungkin untuk mengekspresikan spesifikasi dalam bentuk runnable secara terpisah, yang menambah nilai?

  • Aspek yang lebih jelas dari unit test adalah untuk melacak kesalahan yang tidak dapat diungkapkan melalui analisis statis. Mengingat bahwa jenis kode fungsional aman adalah alat yang baik untuk mengkodekan sangat dekat dengan apa yang dipahami oleh penganalisa statis Anda, tampaknya Anda dapat mengubah banyak keamanan ke arah analisis statis. Namun kesalahan sederhana seperti menggunakan xalih-alih y(keduanya merupakan koordinat) dalam kode Anda tidak dapat dibahas. OTOH kesalahan seperti itu juga bisa muncul saat menulis kode tes, jadi saya tidak yakin apakah itu sepadan dengan usaha.

  • Tes unit memang memperkenalkan redundansi, yang berarti bahwa ketika persyaratan berubah, kode yang mengimplementasikannya dan tes yang mencakup kode ini harus diubah. Overhead ini tentu saja konstan, jadi orang bisa berargumen bahwa itu tidak masalah. Faktanya, dalam bahasa seperti Ruby, itu benar-benar tidak sebanding dengan manfaatnya, tetapi mengingat bagaimana pemrograman fungsional yang diketik secara statis mencakup banyak tes unit darat yang dimaksudkan, rasanya seperti overhead konstan yang dapat dengan mudah dikurangi tanpa penalti.

Dari sini saya menyimpulkan bahwa tes unit agak usang dalam gaya pemrograman ini. Tentu saja klaim semacam itu hanya dapat menyebabkan perang agama, jadi izinkan saya meringkasnya menjadi pertanyaan sederhana:

Ketika Anda menggunakan gaya pemrograman seperti itu, sampai sejauh mana Anda menggunakan tes unit dan mengapa (kualitas apa yang ingin Anda peroleh untuk kode Anda)? Atau sebaliknya: apakah Anda memiliki kriteria di mana Anda dapat memenuhi syarat satu unit kode fungsional yang diketik secara statis sebagaimana dicakup oleh penganalisa statis dan karenanya tanpa perlu cakupan uji unit?

back2dos
sumber
4
Omong-omong, jika Anda belum mencoba QuickCheck , Anda pasti harus melakukannya.
Jon Purdy
scalacheck.org adalah padanan Scala
V-Lamp

Jawaban:

8

Salah satu aspek dari unit test adalah memiliki spesifikasi dalam bentuk runnable. Namun ketika menggunakan gaya deklaratif, yang secara langsung memetakan spesifikasi yang diformalkan ke semantik bahasa, apakah bahkan mungkin untuk mengekspresikan spesifikasi dalam bentuk runnable secara terpisah, yang menambah nilai?

Jika Anda memiliki spesifikasi yang dapat dipetakan langsung ke deklarasi fungsi - baik. Tetapi biasanya itu adalah dua tingkat abstraksi yang sangat berbeda. Tes unit dimaksudkan untuk menguji potongan kode tunggal, ditulis sebagai tes kotak putih oleh pengembang yang sama yang sedang mengerjakan fungsi. Spesifikasi biasanya terlihat seperti "ketika saya memasukkan nilai ini di sini dan menekan tombol ini, ini dan itu harus terjadi". Biasanya spesifikasi semacam itu mengarah pada lebih dari satu fungsi untuk dikembangkan dan diuji.

Namun kesalahan sederhana seperti menggunakan x bukan y (keduanya menjadi koordinat) dalam kode Anda tidak dapat dicakup. Namun kesalahan seperti itu juga bisa muncul saat menulis kode tes, jadi saya tidak yakin apakah itu sepadan dengan usaha.

Kesalahpahaman Anda di sini bahwa unit test sebenarnya untuk menemukan bug di kode Anda secara langsung - itu tidak benar, setidaknya, hanya sebagian yang benar. Mereka dibuat untuk mencegah Anda memperkenalkan bug di lain waktu ketika kode Anda berkembang. Jadi, ketika Anda pertama kali menguji fungsi Anda dan uji unit Anda berfungsi (dengan "x" dan "y" terpasang dengan benar), lalu saat refactoring Anda menggunakan x alih-alih y, maka unit test akan menunjukkan hal itu terserah Anda.

Tes unit memang memperkenalkan redundansi, yang berarti bahwa ketika persyaratan berubah, kode yang mengimplementasikannya dan tes yang mencakup kode ini harus diubah. Overhead ini tentu saja tentang konstan, jadi orang bisa berargumen, bahwa itu tidak terlalu penting. Faktanya, dalam bahasa seperti Ruby, itu benar-benar tidak sebanding dengan manfaatnya, tetapi mengingat bagaimana pemrograman fungsional yang diketik secara statis mencakup banyak tes unit darat yang dimaksudkan, rasanya seperti overhead konstan yang dapat dengan mudah dikurangi tanpa penalti.

Dalam rekayasa, sebagian besar sistem keselamatan bergantung pada redundansi. Sebagai contoh, dua istirahat di dalam mobil, parasut redundan untuk penyelam langit dll. Gagasan yang sama berlaku untuk unit test. Tentu saja, memiliki lebih banyak kode untuk diubah ketika persyaratan berubah dapat menjadi kerugian. Jadi terutama dalam unit test, penting untuk menjaga mereka KERING (ikuti prinsip "Jangan Ulangi Diri Sendiri"). Dalam bahasa yang diketik secara statis, Anda mungkin harus menulis beberapa tes unit kurang dari dalam bahasa yang diketik dengan lemah. Terutama tes "formal" mungkin tidak diperlukan - yang merupakan hal yang baik, karena memberi Anda lebih banyak waktu untuk mengerjakan tes unit penting yang menguji hal-hal yang substansial. Dan jangan berpikir hanya karena Anda tipe statis, Anda tidak perlu tes unit, masih ada banyak ruang untuk memperkenalkan bug saat refactoring.

Doc Brown
sumber
5

Salah satu aspek dari unit test adalah memiliki spesifikasi dalam bentuk runnable. Namun ketika menggunakan gaya deklaratif, yang secara langsung memetakan spesifikasi yang diformalkan ke semantik bahasa, apakah bahkan mungkin untuk mengekspresikan spesifikasi dalam bentuk runnable secara terpisah, yang menambah nilai?

Sangat tidak mungkin bahwa Anda akan dapat sepenuhnya mengekspresikan spesifikasi Anda sebagai batasan tipe.

Ketika Anda menggunakan gaya pemrograman seperti itu, sampai sejauh mana Anda menggunakan tes unit dan mengapa (kualitas apa yang ingin Anda peroleh untuk kode Anda)?

Bahkan, satu manfaat utama dari gaya ini adalah bahwa fungsi murni lebih mudah untuk unit-test: tidak perlu mengatur keadaan eksternal atau memeriksanya setelah eksekusi.

Seringkali spesifikasi (atau bagiannya) dari suatu fungsi dapat dinyatakan sebagai properti yang berkaitan dengan nilai yang dikembalikan ke argumen. Dalam hal ini menggunakan QuickCheck (untuk Haskell) atau ScalaCheck (untuk Scala) dapat membuat Anda menuliskan properti ini sebagai ekspresi bahasa dan memeriksa apakah mereka memiliki input acak.

Alexey Romanov
sumber
1
Sedikit lebih detail tentang QuickCheck: ide dasarnya adalah Anda menulis "properti" (invarian dalam kode Anda) dan menentukan cara menghasilkan input potensial. Quickcheck kemudian membuat satu ton input acak dan memastikan invarian Anda berlaku di setiap kasus. Ini lebih menyeluruh daripada pengujian unit.
Tikhon Jelvis
1

Anda dapat menganggap tes unit sebagai contoh cara menggunakan kode, bersama dengan deskripsi mengapa itu berharga.

Inilah sebuah contoh, di mana Paman Bob cukup baik untuk berpasangan dengan saya di "Game of Life" John Conway . Saya pikir ini latihan yang bagus untuk hal semacam ini. Sebagian besar tes adalah sistem lengkap, menguji seluruh permainan, tetapi yang pertama menguji hanya satu fungsi - yang menghitung tetangga di sekitar sel. Anda dapat melihat bahwa semua tes ditulis dalam bentuk deklaratif, dengan perilaku yang kami cari dinyatakan dengan jelas.

Dimungkinkan juga untuk mengejek fungsi yang digunakan dalam fungsi; baik dengan mengirimkannya ke fungsi (setara dengan injeksi ketergantungan), atau dengan kerangka kerja seperti Brian Marick's Midje .

Lunivore
sumber
0

Ya, unit test sudah masuk akal dengan kode fungsional yang diketik secara statis. Contoh sederhana:

prop_encode a = (decode . encode $ a) == a

Anda bisa memaksa prop_encodedengan tipe statis.

Zhen
sumber