Saya memiliki kelas yang dimaksudkan untuk menghasilkan kata sandi acak dengan panjang yang juga acak, tetapi terbatas antara min yang ditentukan dan panjang maks.
Saya sedang membangun unit test, dan mengalami hambatan kecil yang menarik dengan kelas ini. Seluruh ide di balik unit test adalah harus diulang. Jika Anda menjalankan tes seratus kali, itu akan memberikan hasil yang sama seratus kali. Jika Anda bergantung pada beberapa sumber daya yang mungkin ada atau tidak ada di sana atau mungkin atau mungkin tidak dalam kondisi awal yang Anda harapkan, maka Anda harus mengejek sumber daya yang dimaksud untuk memastikan bahwa pengujian Anda benar-benar selalu dapat diulang.
Tetapi bagaimana dalam kasus-kasus di mana SUT seharusnya menghasilkan keluaran yang tidak ditentukan?
Jika saya memperbaiki panjang min dan maks ke nilai yang sama maka saya dapat dengan mudah memeriksa bahwa kata sandi yang dihasilkan memiliki panjang yang diharapkan. Tetapi jika saya menentukan rentang panjang yang dapat diterima (katakanlah 15 - 20 karakter), maka Anda sekarang memiliki masalah bahwa Anda dapat menjalankan tes seratus kali dan mendapatkan 100 lintasan tetapi pada lari ke-101 Anda mungkin mendapatkan string 9 karakter kembali.
Dalam hal kelas kata sandi, yang intinya cukup sederhana, seharusnya tidak membuktikan masalah besar. Tapi itu membuat saya berpikir tentang kasus umum. Apa strategi yang biasanya diterima sebagai yang terbaik untuk diambil ketika berhadapan dengan SUT yang menghasilkan output yang tidak ditentukan oleh desain?
sumber
Jawaban:
Output "Non-deterministik" harus memiliki cara menjadi deterministik untuk keperluan pengujian unit. Salah satu cara untuk menangani keacakan adalah dengan memungkinkan penggantian mesin acak. Berikut ini sebuah contoh (PHP 5.3+):
Anda dapat membuat versi uji khusus dari fungsi yang mengembalikan urutan angka apa pun yang Anda ingin memastikan pengujian sepenuhnya dapat diulang. Dalam program nyata, Anda dapat memiliki implementasi default yang bisa menjadi mundur jika tidak diganti.
sumber
Kata sandi keluaran aktual mungkin tidak ditentukan setiap kali metode dijalankan, tetapi masih akan memiliki fitur menentukan yang dapat diuji, seperti panjang minimum, karakter yang termasuk dalam rangkaian karakter yang ditentukan, dll.
Anda juga dapat menguji apakah rutin mengembalikan hasil yang pasti setiap kali dengan seeding generator kata sandi Anda dengan nilai yang sama setiap kali.
sumber
Uji terhadap "kontrak". Ketika metode didefinisikan sebagai "menghasilkan kata sandi dengan panjang 15 hingga 20 karakter dengan az", ujilah dengan cara ini
Tambahan Anda dapat mengekstrak generasi, jadi semuanya, yang bergantung padanya, dapat diuji menggunakan kelas generator "statis" lainnya
sumber
Anda memiliki
Password generator
dan Anda membutuhkan sumber acak.Seperti yang Anda nyatakan dalam pertanyaan a
random
membuat keluaran non-deterministik karena merupakan keadaan global . Berarti itu mengakses sesuatu di luar sistem untuk menghasilkan nilai.Anda tidak pernah bisa menghilangkan sesuatu seperti itu untuk semua kelas Anda, tetapi Anda dapat memisahkan pembuatan kata sandi untuk pembuatan nilai acak.
Jika Anda menyusun kode seperti ini, Anda dapat mengejek
RandomSource
tes Anda.Anda tidak akan dapat menguji 100%
RandomSource
tetapi saran yang Anda dapatkan untuk menguji nilai-nilai dalam pertanyaan ini dapat diterapkan untuk itu (Seperti pengujian yangrand->(1,26);
selalu mengembalikan angka dari 1 hingga 26.sumber
Dalam kasus fisika partikel Monte Carlo, saya telah menulis "unit test" {*} yang memanggil rutin non-deterministik dengan seed acak yang telah ditetapkan , dan kemudian menjalankan sejumlah statistik kali dan memeriksa pelanggaran kendala (level energi) di atas energi input harus tidak dapat diakses, semua lintasan harus memilih beberapa level, dll) dan regresi terhadap hasil yang direkam sebelumnya.
{*} Tes semacam itu melanggar prinsip "buat tes cepat" untuk pengujian unit, jadi Anda mungkin merasa lebih baik mengkarakterisasinya dengan beberapa cara lain: tes penerimaan atau tes regresi, misalnya. Namun, saya menggunakan kerangka pengujian unit saya.
sumber
Saya harus tidak setuju dengan jawaban yang diterima , karena dua alasan:
(Perhatikan bahwa itu mungkin jawaban yang baik dalam banyak situasi, tetapi tidak dalam semua, dan mungkin tidak dalam kebanyakan.)
Jadi apa yang saya maksud dengan itu? Nah, dengan overfitting yang saya maksud adalah masalah khas pengujian statistik: overfitting terjadi ketika Anda menguji algoritma stokastik terhadap sekumpulan data yang terlalu terbatas. Jika kemudian Anda kembali dan memperbaiki algoritme Anda, Anda secara implisit akan membuatnya cocok dengan data pelatihan dengan sangat baik (Anda secara tidak sengaja menyesuaikan algoritme Anda dengan data uji), tetapi semua data lain mungkin tidak sama sekali (karena Anda tidak pernah menguji hal itu) .
(Kebetulan, ini selalu merupakan masalah yang mengintai pengujian unit. Inilah sebabnya mengapa tes yang baik selesai , atau setidaknya mewakili untuk unit yang diberikan, dan ini sulit secara umum.)
Jika Anda membuat pengujian Anda deterministik dengan membuat generator angka acak yang dapat dicolokkan, Anda selalu menguji terhadap kumpulan data yang sangat kecil dan (biasanya) tidak representatif . Ini memiringkan data Anda dan dapat menyebabkan bias dalam fungsi Anda.
Poin kedua, ketidakpraktisan, muncul ketika Anda tidak memiliki kendali atas variabel stokastik. Ini biasanya tidak terjadi dengan generator bilangan acak (kecuali jika Anda memerlukan sumber acak "nyata") tetapi itu bisa terjadi ketika stokastik menyelinap ke dalam masalah Anda dengan cara lain. Misalnya, ketika menguji kode bersamaan: kondisi balapan selalu stokastik, Anda tidak dapat (dengan mudah) membuatnya menjadi deterministik.
Satu-satunya cara untuk meningkatkan kepercayaan diri dalam kasus-kasus itu adalah dengan banyak tes . Busa, bilas, ulangi. Ini meningkatkan kepercayaan diri, hingga tingkat tertentu (di mana trade-off untuk uji coba tambahan menjadi diabaikan).
sumber
Anda sebenarnya memiliki banyak tanggung jawab di sini. Pengujian unit dan khususnya TDD sangat bagus untuk menyoroti hal semacam ini.
Tanggung jawabnya adalah:
1) pembangkit bilangan acak. 2) Pemformat kata sandi.
Pemformat kata sandi menggunakan penghasil angka acak. Suntikkan generator ke formatter Anda melalui konstruktornya sebagai antarmuka. Sekarang Anda dapat sepenuhnya menguji generator nomor acak Anda (uji statistik) dan Anda dapat menguji formatter dengan menyuntikkan generator nomor acak yang diejek.
Anda tidak hanya mendapatkan kode yang lebih baik, Anda juga mendapatkan tes yang lebih baik.
sumber
Seperti yang lain telah disebutkan, Anda menguji kode ini dengan menghapus keacakan.
Anda mungkin juga ingin memiliki tes tingkat lebih tinggi yang membiarkan generator nomor acak tetap di tempatnya, menguji hanya kontrak (panjang kata sandi, karakter yang diizinkan, ...) dan, jika gagal, membuang informasi yang cukup untuk memungkinkan Anda mereproduksi sistem menyatakan dalam satu contoh di mana tes acak gagal.
Tidak masalah bahwa tes itu sendiri tidak dapat diulang - selama Anda dapat menemukan alasan mengapa tes gagal kali ini.
sumber
Banyak kesulitan pengujian unit menjadi sepele ketika Anda refactor kode Anda untuk memutuskan dependensi. Basis data, sistem file, pengguna, atau dalam kasus Anda, sumber keacakan.
Cara lain untuk melihat adalah bahwa unit test seharusnya menjawab pertanyaan "apakah kode ini melakukan apa yang saya inginkan?". Dalam kasus Anda, Anda tidak tahu apa yang ingin Anda lakukan kode karena itu non-deterministik.
Dengan pikiran ini, pisahkan logika Anda menjadi bagian-bagian kecil, mudah dipahami, dan mudah diuji. Khususnya, Anda membuat metode yang berbeda (atau kelas!) Yang mengambil sumber keacakan sebagai inputnya, dan menghasilkan kata sandi sebagai output. Kode itu jelas deterministik.
Dalam pengujian unit Anda, Anda memberinya input tidak-cukup-acak yang sama setiap kali. Untuk aliran acak yang sangat kecil, cukup hard-code nilai-nilai dalam pengujian Anda. Jika tidak, berikan benih konstan ke RNG dalam pengujian Anda.
Pada tingkat pengujian yang lebih tinggi (menyebutnya "penerimaan" atau "integrasi" atau apa pun), Anda akan membiarkan kode berjalan dengan sumber acak yang sebenarnya.
sumber
Sebagian besar jawaban di atas menunjukkan bahwa mengejek generator nomor acak adalah cara untuk pergi, namun saya hanya menggunakan fungsi mt_rand bawaan. Mengizinkan mengejek berarti menulis ulang kelas untuk meminta generator nomor acak disuntikkan pada waktu konstruksi.
Atau begitulah yang saya pikirkan!
Salah satu konsekuensi dari penambahan ruang nama adalah bahwa mengejek yang dibangun dalam fungsi PHP telah berubah dari sangat sulit menjadi sangat sederhana. Jika SUT berada dalam namespace yang diberikan maka yang perlu Anda lakukan adalah mendefinisikan fungsi mt_rand Anda sendiri dalam tes unit di bawah namespace itu, dan itu akan digunakan sebagai ganti fungsi built in PHP selama durasi tes.
Berikut ini adalah rangkaian uji yang diselesaikan:
Saya pikir saya akan menyebutkan ini, karena mengesampingkan fungsi internal PHP adalah penggunaan lain untuk ruang nama yang tidak terpikir oleh saya. Terima kasih kepada semua orang untuk bantuannya.
sumber
Ada tes tambahan yang harus Anda sertakan dalam situasi ini, dan itu adalah satu untuk memastikan bahwa panggilan berulang ke pembuat kata sandi benar-benar menghasilkan kata sandi yang berbeda. Jika Anda memerlukan pembuat kata sandi yang aman, Anda juga harus menguji panggilan simultan menggunakan beberapa utas.
Ini pada dasarnya memastikan bahwa Anda menggunakan fungsi acak Anda dengan benar, dan tidak melakukan seeding ulang pada setiap panggilan.
sumber