Pengujian: deterministik atau non-deterministik?

17

Apakah lebih baik memiliki a

  • Rangkaian tes deterministik, yang menghasilkan tes yang sama berhasil
  • Rangkaian uji non-deterministik, yang berpotensi mencakup lebih banyak kasus

?

Contoh: Anda menulis rangkaian uji untuk menguji fungsionalitas pengontrol di aplikasi MVC. Pengontrol membutuhkan data aplikasi dari database sebagai input selama pengujian. Ada dua opsi untuk melakukan ini:

  • Anda menyandikan baris mana dari database tes yang dipilih sebagai input (mis. Baris ke-10 dan ke-4)
  • Anda menggunakan generator nomor acak untuk secara acak mengambil data dari database (dua baris dipilih oleh generator nomor acak)

Yang pertama adalah deterministik: setiap uji untuk revisi kode yang sama harus menghasilkan hasil yang sama. Yang kedua adalah non-deterministik: setiap rangkaian test suite memiliki kemungkinan untuk menghasilkan hasil yang berbeda. Namun data yang dipilih secara acak mungkin merupakan representasi yang lebih baik dari kasus tepi data. Mungkin mensimulasikan pengguna yang memberi makan pengontrol kami dengan data yang tidak dapat diprediksi lebih baik?

Apa alasan untuk memilih satu dari yang lain?

DCKing
sumber
5
Tes itu gagal, kadang-kadang. martinfowler.com/articles/nonDeterminism.html
Terima kasih atas tautannya. Dengan mengingat artikel itu, saya merasa perlu mengklarifikasi bahwa non-determinisme berarti dalam konteks test suite ini. Karena data dipilih secara acak dari database, semua data yang diumpankan ke controller adalah data yang valid secara default. Ini berarti bahwa negatif palsu tidak ada dalam test suite ketika datang ke non determinisme. Di satu sisi, keacakan ini mensimulasikan pengguna memilih data 'secara acak' untuk digunakan dalam pengontrol. Ini belum tentu sama non-determinisme yang dibahas artikel ini, kan?
DCKing
10
@ DC King: Pertimbangkan apa yang terjadi jika tes Anda gagal. Oke, Anda punya bug. Eh, sekarang bagaimana? Jalankan lagi dalam mode debug! Di mana itu berhasil! Seperti yang dilakukannya ratusan kali berikutnya Anda menjalankannya, dan kemudian Anda menulis masalah sebagai pemogokan sinar kosmik. Ketidakpastian dalam tes terdengar sangat tidak bisa dijalankan. Jika Anda merasa perlu untuk menutupi lebih banyak ground dalam test case Anda, tutupi lebih banyak ground. Initilise RNG Anda dengan set seed dan jalankan "test" beberapa ratus kali dengan nilai acak yang konsisten.
Phoshi
1
(akhirnya tiba di mesin tempat saya dapat mencari twitter dengan benar - " Tes itu kadang-kadang gagal " berasal dari #FiveWordTechHorrors di Twitter - ingin memberi kredit dengan benar)

Jawaban:

31

Ketika setiap rangkaian test suite memberi Anda kemungkinan untuk menghasilkan hasil yang berbeda, tes ini hampir sama sekali tidak berharga - ketika suite menunjukkan Anda bug, Anda memiliki kemungkinan besar bahwa Anda tidak dapat mereproduksinya, dan ketika Anda mencoba untuk memperbaiki bug, Anda tidak dapat memverifikasi apakah perbaikan Anda berhasil.

Jadi ketika Anda berpikir Anda perlu menggunakan semacam generator angka acak untuk menghasilkan data pengujian Anda, pastikan Anda selalu menginisialisasi generator dengan seed yang sama, atau bertahan data uji acak Anda dalam file sebelum memasukkannya ke dalam tes Anda, sehingga Anda dapat menjalankan kembali pengujian dengan tepat dengan data yang sama dari proses sebelumnya. Dengan cara ini, Anda dapat mengubah tes non-deterministik menjadi tes deterministik.

EDIT: Menggunakan generator angka acak untuk mengambil beberapa data uji adalah IMHO kadang-kadang pertanda terlalu malas memilih data uji yang bagus . Alih-alih melemparkan 100.000 nilai tes yang dipilih secara acak dan berharap bahwa ini akan cukup untuk menemukan semua bug serius secara kebetulan, lebih baik gunakan otak Anda, pilih 10 hingga 20 kasus "menarik", dan gunakan untuk test suite. Ini tidak hanya menghasilkan kualitas tes yang lebih baik, tetapi juga kinerja suite yang jauh lebih tinggi.

Doc Brown
sumber
Terima kasih atas jawaban anda. Apa pendapat Anda tentang komentar yang saya berikan pada pertanyaan saya?
DCKing
1
@ DC King: jika Anda benar-benar berpikir generator acak akan lebih baik dalam memilih kasus uji yang baik daripada Anda (apa yang saya ragu), gunakan sekali untuk menemukan kombinasi data uji di mana program Anda gagal, dan menempatkan kombinasi-kombinasi itu ke bagian "hardcoded" dari ruang tes Anda.
Doc Brown
Terima kasih lagi. Memperbarui jawaban saya sehingga sepertinya tidak berlaku untuk aplikasi MVC saja.
DCKing
1
Dalam beberapa konteks UI (misalnya, game mengambil input controller) yang memiliki program pengujian yang menghasilkan entri kunci acak dapat berguna untuk pengujian stres. Mereka dapat mengungkap cacat yang sulit ditemukan dengan masukan yang disengaja.
Gort the Robot
@ SvenvenBurnap: baik, cara saya memahami pertanyaan, saya pikir OP memiliki pengujian regresi yang lebih konvensional dalam pikiran. Tentu saja, saya setuju, pengujian stres adalah kasus khusus yang mungkin juga tergantung pada perangkat keras dan menghasilkan perilaku yang tidak deterministik bahkan ketika Anda tidak menggunakan generator acak. Itu sesuatu yang dijelaskan dalam artikel yang ditautkan oleh MichaelT di komentar pertama di bawah pertanyaan. Dan bahkan dalam pengujian stres dengan input acak, orang setidaknya bisa mencoba membuat perilaku lebih deterministik dengan menggunakan benih acak yang ditentukan.
Doc Brown
4

Baik deterministik dan non-deterministik memiliki tempat

Saya akan membaginya sebagai berikut:

Tes unit.

Ini harus memiliki tes deterministik, berulang dengan data yang sama persis setiap waktu. Tes unit menyertai bagian kode yang spesifik dan terisolasi dan harus mengujinya secara deterministik.

Tes stres fungsional dan input.

Ini dapat menggunakan pendekatan non-deterministik dengan peringatan berikut:

  • fakta itu jelas digambarkan dan diserukan
  • nilai acak yang dipilih dicatat dan dapat dicoba kembali secara manual
Michael Durrant
sumber
3

Kedua.

Tes deterministik dan nondeterministik memiliki berbagai kasus penggunaan dan nilai yang berbeda untuk paket Anda. Umumnya nondeterministic tidak dapat memberikan presisi yang sama dengan pengujian deterministik, yang perlahan-lahan tumbuh menjadi "pengujian nondeterministic tidak memberikan nilai." Ini salah. Mereka mungkin kurang tepat, tetapi mereka juga bisa jauh lebih luas, yang memiliki manfaatnya sendiri.

Mari kita ambil contoh: Anda menulis fungsi yang mengurutkan daftar bilangan bulat. Apa yang akan menjadi beberapa tes unit deterministik yang menurut Anda berguna?

  • Daftar kosong
  • Daftar hanya dengan satu elemen
  • Daftar dengan semua elemen yang sama
  • Daftar dengan beberapa elemen unik
  • Daftar dengan banyak elemen, beberapa di antaranya adalah duplikat
  • Daftar dengan NaN,INT_MIN , danINT_MAX
  • Daftar yang sudah diurutkan sebagian
  • Daftar dengan 10.000.000 elemen

Dan itu hanya fungsi penyortiran! Tentu, Anda dapat berargumen bahwa beberapa di antaranya tidak perlu, atau beberapa di antaranya dapat dikesampingkan dengan alasan informal. Tapi kami insinyur dan kami melihat alasan informal meledak di wajah kami. Kami tahu kami tidak cukup pintar untuk sepenuhnya memahami sistem yang kami bangun atau sepenuhnya menyimpan kompleksitas di kepala kami. Itu sebabnya kami menulis tes di tempat pertama. Menambahkan pengujian nondeterministik hanya mengatakan bahwa kita mungkin tidak harus cukup pintar untuk mengetahui semua tes yang baik secara apriori. Dengan melemparkan data semi-acak ke dalam fungsi Anda, Anda akan lebih mungkin menemukan casing tepi yang Anda lewatkan.

Tentu saja, itu tidak mengesampingkan pengujian deterministik. Pengujian nondeterministic membantu menemukan bug di petak besar program. Namun, begitu Anda menemukan bug, Anda membutuhkan cara yang dapat direproduksi untuk menunjukkan bahwa Anda telah memperbaikinya. Begitu:

  • Gunakan tes nondeterministic untuk menemukan bug dalam kode Anda.
  • Gunakan tes deterministik untuk memverifikasi perbaikan dalam kode Anda.

Perhatikan bahwa ini berarti banyak saran kuat tentang tes unit tidak selalu berlaku untuk tes nondeterministik. Misalnya, mereka harus cepat. Tes properti tingkat rendah harus cepat, tetapi tes nondeterministik seperti "mensimulasikan pengguna mengklik tombol secara acak di situs web Anda dan pastikan Anda tidak pernah mendapatkan 500 kesalahan" harus mendukung kelengkapan daripada kecepatan. Hanya memiliki tes seperti itu yang berjalan secara independen dari proses build Anda sehingga tidak memperlambat pengembangan. Misalnya, jalankan di kotak pementasan pribadi.

Hovercouch
sumber
-1

Anda tidak benar-benar ingin deterministik vs non-deterministik.

Apa yang Anda inginkan adalah "selalu sama" vs. "tidak selalu sama".

Misalnya, Anda mungkin memiliki nomor build yang meningkat dengan setiap build, dan ketika Anda menginginkan beberapa nomor acak, Anda menginisialisasi generator angka acak dengan nomor build sebagai seed. Jadi setiap build, Anda melakukan pengujian dengan nilai yang berbeda, memberi Anda lebih banyak peluang untuk menemukan bug.

Tetapi begitu bug ditemukan, yang perlu Anda lakukan adalah menjalankan tes dengan nomor build yang sama, dan itu dapat direproduksi.

gnasher729
sumber
1
Atau jika Anda tidak memiliki nomor build untuk digunakan, tempatkan nilai awal benih dalam output dari uji coba, sehingga Anda dapat kembali menjalankan tes dengan benih yang sama.
RemcoGerlich