Dalam pengujian unit saya, saya sering melemparkan nilai arbitrer ke kode saya untuk melihat apa fungsinya. Misalnya, jika saya tahu foo(1, 2, 3)
seharusnya mengembalikan 17, saya dapat menulis ini:
assertEqual(foo(1, 2, 3), 17)
Angka-angka ini murni arbitrer dan tidak memiliki makna yang lebih luas (mereka tidak, misalnya, syarat batas, meskipun saya juga mengujinya). Saya akan berjuang untuk menghasilkan nama-nama bagus untuk angka-angka ini, dan menulis sesuatu seperti const int TWO = 2;
ini jelas tidak membantu. Apakah boleh menulis tes seperti ini, atau haruskah saya memasukkan angka ke dalam konstanta?
Dalam Apakah semua angka ajaib dibuat sama? , kami belajar bahwa angka ajaib baik-baik saja jika artinya jelas dari konteks, tetapi dalam kasus ini angka-angka itu sebenarnya tidak memiliki arti sama sekali.
sumber
1, 2, 3
adalah indeks array 3D di mana Anda sebelumnya menyimpan nilai17
, maka saya pikir tes ini akan bagus (selama Anda memiliki beberapa tes negatif juga). Tetapi jika itu adalah hasil perhitungan, Anda harus memastikan bahwa siapa pun yang membaca tes ini akan mengerti mengapafoo(1, 2, 3)
harus demikian17
, dan angka ajaib mungkin tidak akan mencapai tujuan itu.const int TWO = 2;
bahkan lebih buruk daripada hanya menggunakan2
. Itu sesuai dengan kata-kata aturan dengan niat untuk melanggar semangatnya.foo
, itu tidak akan berarti apa-apa, dan begitu pula parameternya. Namun dalam kenyataannya, saya cukup yakin fungsi tidak memiliki nama itu, dan parameter tidak memiliki namabar1
,bar2
danbar3
. Buat contoh yang lebih realistis di mana nama memiliki makna, maka jauh lebih masuk akal untuk membahas jika nilai data uji juga membutuhkan nama.Jawaban:
Kapan Anda benar-benar memiliki angka yang tidak memiliki makna sama sekali?
Biasanya, ketika angka-angka memiliki makna, Anda harus menugaskan mereka ke variabel lokal dari metode pengujian untuk membuat kode lebih mudah dibaca dan menjelaskan diri sendiri. Nama-nama variabel setidaknya harus mencerminkan apa arti variabel, tidak harus nilainya.
Contoh:
Perhatikan bahwa variabel pertama tidak bernama
HUNDRED_DOLLARS_ZERO_CENT
, tetapistartBalance
untuk menunjukkan apa arti dari variabel tetapi tidak bahwa nilainya dalam hal apa pun istimewa.sumber
0.05f
keint
. :)const
variabel.calculateCompoundInterest
? Jika demikian, maka pengetikan ekstra adalah bukti kerja bahwa Anda telah membaca dokumentasi untuk fungsi yang Anda uji, atau setidaknya menyalin nama yang diberikan kepada Anda oleh IDE Anda. Saya tidak yakin berapa banyak ini memberi tahu pembaca tentang maksud kode, tetapi jika Anda melewati parameter dalam urutan yang salah setidaknya mereka bisa tahu apa yang dimaksudkan.Jika Anda menggunakan angka arbitrer hanya untuk melihat apa yang mereka lakukan, maka apa yang sebenarnya Anda cari mungkin adalah data uji yang dibuat secara acak, atau pengujian berbasis properti.
Misalnya, Hipotesis adalah pustaka Python keren untuk pengujian semacam ini, dan didasarkan pada QuickCheck .
Idenya adalah untuk tidak membatasi diri Anda dengan nilai-nilai Anda sendiri, tetapi pilih yang acak yang dapat digunakan untuk memeriksa apakah fungsi Anda cocok dengan spesifikasinya. Sebagai catatan penting, sistem ini umumnya akan mengingat setiap input yang gagal, dan kemudian memastikan bahwa input tersebut selalu diuji di masa depan.
Poin 3 dapat membingungkan bagi beberapa orang jadi mari kita perjelas. Itu tidak berarti bahwa Anda memberikan jawaban yang tepat - ini jelas tidak mungkin dilakukan untuk input sewenang-wenang. Alih-alih, Anda menegaskan sesuatu tentang properti hasil. Sebagai contoh, Anda mungkin menegaskan bahwa setelah menambahkan sesuatu ke daftar itu menjadi tidak kosong, atau bahwa pohon pencarian biner menyeimbangkan sebenarnya seimbang (menggunakan kriteria apa pun yang dimiliki struktur data tertentu).
Secara keseluruhan, memilih sendiri angka arbitrer mungkin sangat buruk - itu tidak benar-benar menambah sejumlah besar nilai, dan membingungkan bagi siapa pun yang membacanya. Secara otomatis menghasilkan banyak data uji acak dan menggunakannya secara efektif adalah baik. Menemukan Hipotesis atau pustaka seperti QuickCheck untuk bahasa pilihan Anda mungkin merupakan cara yang lebih baik untuk mencapai tujuan Anda sambil tetap dimengerti oleh orang lain.
sumber
foo
itu komputasi) ...? Jika Anda 100% yakin kode Anda memberikan jawaban yang benar, maka Anda hanya akan memasukkan kode itu dalam program dan tidak mengujinya. Jika tidak, maka Anda perlu menguji, dan saya pikir semua orang melihat ke mana arahnya.d
hari, perhitungan padad
hari + 1 bulan harus diketahui tingkat persentase bulanannya lebih tinggi), dll.Nama tes unit Anda harus memberikan sebagian besar konteksnya. Bukan dari nilai konstanta. Nama / dokumentasi untuk suatu tes harus memberikan konteks yang sesuai dan penjelasan tentang angka ajaib apa pun yang ada dalam tes.
Jika itu tidak cukup, sedikit dokumentasi harus dapat menyediakannya (baik melalui nama variabel atau dokumen). Ingatlah bahwa fungsi itu sendiri memiliki parameter yang diharapkan memiliki nama yang bermakna. Menyalin mereka ke dalam pengujian Anda untuk menyebutkan argumen agak tidak ada gunanya.
Dan terakhir, jika unittests Anda cukup rumit sehingga ini sulit / tidak praktis Anda mungkin memiliki fungsi yang terlalu rumit dan mungkin mempertimbangkan mengapa hal ini terjadi.
Semakin ceroboh Anda menulis tes, semakin buruk kode aktual Anda. Jika Anda merasa perlu memberi nama nilai tes Anda untuk memperjelas tes, itu sangat menyarankan metode Anda yang sebenarnya membutuhkan penamaan dan / atau dokumentasi yang lebih baik. Jika Anda menemukan kebutuhan untuk menyebutkan konstanta dalam tes saya akan melihat mengapa Anda membutuhkan ini - kemungkinan masalahnya bukan tes itu sendiri tetapi implementasi
sumber
Ini sangat tergantung pada fungsi yang Anda uji. Saya tahu banyak kasus di mana angka-angka individual tidak memiliki arti khusus pada mereka sendiri, tetapi kasus uji secara keseluruhan dibangun dengan penuh pertimbangan dan karenanya memiliki makna tertentu. Itulah yang harus didokumentasikan seseorang dalam beberapa cara. Misalnya, jika
foo
benar-benar merupakan metodetestForTriangle
yang memutuskan apakah ketiga angka tersebut mungkin panjang tepian segitiga yang valid, pengujian Anda mungkin terlihat seperti ini:dan seterusnya. Anda dapat meningkatkan ini dan mengubah komentar menjadi parameter pesan
assertEqual
yang akan ditampilkan ketika tes gagal. Anda kemudian dapat meningkatkan ini lebih lanjut dan mengubahnya menjadi tes yang didorong data (jika kerangka pengujian Anda mendukung ini). Namun demikian, Anda dapat membantu diri sendiri jika Anda memasukkan catatan ke dalam kode mengapa Anda memilih angka ini dan dari berbagai perilaku yang Anda uji dengan masing-masing kasus.Tentu saja, untuk fungsi lain, nilai individual untuk parameter mungkin lebih penting, jadi menggunakan nama fungsi yang tidak berarti seperti
foo
ketika menanyakan cara menangani arti parameter mungkin bukan ide terbaik.sumber
Mengapa kita ingin menggunakan Konstanta yang dinamai bukan angka?
Jika Anda menulis beberapa tes unit, masing-masing dengan bermacam-macam 3 Angka (startBalance, bunga, tahun) - Saya hanya akan mengemas nilai-nilai ke dalam unit-test sebagai variabel lokal. Lingkup terkecil tempat mereka berada.
Jika Anda menggunakan bahasa yang memungkinkan parameter bernama, ini tentu saja superflous. Di sana saya hanya akan mengemas nilai mentah dalam pemanggilan metode. Saya tidak bisa membayangkan refactoring membuat pernyataan ini lebih ringkas:
Atau gunakan Kerangka Pengujian, yang akan memungkinkan Anda untuk menentukan kasus pengujian dalam beberapa array atau format Peta:
sumber
Angka-angka yang digunakan untuk memanggil metode jadi pasti premis di atas tidak benar. Anda mungkin tidak peduli berapa jumlahnya tetapi itu tidak penting. Ya, Anda dapat menyimpulkan angka-angka yang digunakan oleh beberapa sihir IDE tetapi akan jauh lebih baik jika Anda hanya memberikan nama nilai - bahkan jika mereka hanya cocok dengan parameternya.
sumber
assertEqual "Returned value" (makeKindInt 42) (runTest "lvalue_operators")
). Dalam contoh ini,42
hanya nilai placeholder yang dihasilkan oleh kode dalam skrip uji yang dinamailvalue_operators
dan kemudian diperiksa saat dikembalikan oleh skrip. Tidak ada artinya sama sekali, selain itu nilai yang sama terjadi di dua tempat yang berbeda. Apa yang akan menjadi nama yang tepat di sini yang sebenarnya memberikan makna yang bermanfaat?Jika Anda ingin menguji fungsi murni pada satu set input yang bukan kondisi batas, maka Anda hampir pasti ingin mengujinya pada sejumlah set input yang tidak (dan sedang) kondisi batas. Dan bagi saya itu berarti harus ada tabel nilai untuk memanggil fungsi, dan sebuah loop:
Alat-alat seperti yang disarankan dalam jawaban Dannnno dapat membantu Anda menyusun tabel nilai untuk diuji.
bar
,,baz
danblurf
harus diganti dengan nama-nama yang bermakna seperti dibahas dalam jawaban Philipp .(Prinsip umum yang bisa diperdebatkan di sini: Angka tidak selalu "angka ajaib" yang perlu nama; sebaliknya, angka mungkin data . Jika masuk akal untuk memasukkan angka Anda ke dalam array, mungkin array catatan, maka mereka mungkin data Sebaliknya, jika Anda menduga Anda mungkin memiliki data di tangan Anda, pertimbangkan untuk memasukkannya ke dalam array dan memperoleh lebih banyak dari itu.)
sumber
Tes berbeda dari kode produksi dan, setidaknya dalam tes unit ditulis dalam Spock, yang singkat dan to the point, saya tidak punya masalah menggunakan konstanta sihir.
Jika suatu tes panjangnya 5 baris, dan mengikuti skema dasar diberikan / kapan / kemudian, mengekstraksi nilai-nilai tersebut ke dalam konstanta hanya akan membuat kode lebih panjang dan lebih sulit untuk dibaca. Jika logikanya adalah "Ketika saya menambahkan pengguna bernama Smith, saya melihat pengguna Smith kembali dalam daftar pengguna", tidak ada gunanya mengekstraksi "Smith" ke sebuah konstanta.
Ini tentu saja berlaku jika Anda dapat dengan mudah mencocokkan nilai yang digunakan di blok "diberikan" (setup) dengan yang ditemukan di blok "kapan" dan "kemudian". Jika pengaturan pengujian Anda dipisahkan (dalam kode) dari tempat data digunakan, mungkin lebih baik menggunakan konstanta. Tetapi karena tes terbaik dilakukan sendiri, pengaturan biasanya dekat dengan tempat penggunaan dan kasus pertama berlaku, yang berarti konstanta sihir cukup dapat diterima dalam kasus ini.
sumber
Pertama mari kita sepakat bahwa "unit test" sering digunakan untuk mencakup semua tes otomatis yang ditulis oleh seorang programmer, dan tidak ada gunanya memperdebatkan apa yang harus disebut setiap tes ....
Saya telah bekerja pada sistem di mana perangkat lunak mengambil banyak input dan bekerja di luar "solusi" yang harus memenuhi beberapa kendala, sementara mengoptimalkan angka lainnya. Tidak ada jawaban yang benar, sehingga perangkat lunak hanya harus memberikan jawaban yang masuk akal.
Itu melakukan ini dengan menggunakan banyak angka acak untuk mendapatkan titik awal, kemudian menggunakan "pendaki bukit" untuk meningkatkan hasilnya. Ini dijalankan berkali-kali, memilih hasil terbaik. Generator bilangan acak dapat diunggulkan, sehingga selalu memberikan angka yang sama dalam urutan yang sama, maka jika tes menetapkan seed, kita tahu bahwa hasilnya akan sama pada setiap run.
Kami memiliki banyak tes yang melakukan hal di atas, dan memeriksa bahwa hasilnya sama, ini memberi tahu kami bahwa kami tidak mengubah apa yang dilakukan bagian sistem secara tidak sengaja saat refactoring dll. Tidak memberi tahu kami apa pun tentang kebenaran dari apa yang bagian dari sistem lakukan.
Tes-tes ini mahal untuk dipertahankan, karena setiap perubahan pada kode pengoptimalan akan mematahkan tes, tetapi mereka juga menemukan beberapa bug dalam kode yang jauh lebih besar yang melakukan pra-pemrosesan data, dan pasca-memproses hasilnya.
Saat kami "mengejek" database, Anda bisa menyebut tes ini "tes unit", tetapi "unit" itu agak besar.
Seringkali ketika Anda bekerja pada sistem tanpa tes, Anda melakukan sesuatu seperti di atas, sehingga Anda dapat mengonfirmasi refactoring Anda tidak mengubah output; semoga tes yang lebih baik ditulis untuk kode baru!
sumber
Saya pikir dalam kasus ini angka-angka harus disebut Angka Sewenang-wenang, bukan Angka Ajaib, dan hanya berkomentar garis sebagai "kasus uji sewenang-wenang".
Tentu saja, beberapa Angka Ajaib juga dapat berubah-ubah, seperti untuk nilai "pegangan" yang unik (yang harus diganti dengan konstanta bernama, tentu saja), tetapi juga dapat dihitung sebagai konstanta seperti "kecepatan udara burung pipit Eropa yang tidak dikenali dalam furlongs per dua minggu", di mana nilai numerik dicolokkan tanpa komentar atau konteks bermanfaat.
sumber
Saya tidak akan berani mengatakan ya / tidak, tapi di sini ada beberapa pertanyaan yang harus Anda tanyakan pada diri sendiri ketika memutuskan apakah itu OK atau tidak.
Jika angkanya tidak berarti apa-apa, mengapa mereka ada di tempat pertama? Bisakah mereka diganti dengan yang lain? Bisakah Anda melakukan verifikasi berdasarkan pada pemanggilan metode dan aliran alih-alih pernyataan nilai? Pertimbangkan sesuatu seperti
verify()
metode Mockito yang memeriksa apakah pemanggilan metode tertentu dilakukan untuk mengolok-olok objek dan bukannya benar-benar menegaskan nilai.Jika angka-angka memang berarti sesuatu, maka mereka harus ditugaskan ke variabel yang diberi nama dengan tepat.
Menulis nomor
2
sebagaiTWO
mungkin bermanfaat dalam konteks tertentu, dan tidak begitu banyak dalam konteks lain.assertEquals(TWO, half_of(FOUR))
masuk akal bagi seseorang yang membaca kode. Segera jelas apa yang Anda uji.assertEquals(numCustomersInBank(BANK_1), TWO)
, maka ini tidak membuat yang banyak akal. Mengapa tidakBANK_1
mengandung dua pelanggan? Untuk apa kita menguji?sumber