Apakah ada teori formal / matematika pengujian perangkat lunak?

12

"Teori pengujian perangkat lunak" Googling tampaknya hanya memberikan teori dalam arti kata yang lembut; Saya belum dapat menemukan apa pun yang akan diklasifikasikan sebagai teori dalam matematika, informasi teoretis atau pengertian bidang ilmiah lainnya.

Apa yang saya cari adalah sesuatu yang memformalkan pengujian itu, pengertian yang digunakan, apa itu ujian, kelayakan menguji sesuatu, kepraktisan menguji sesuatu, sejauh mana sesuatu harus diuji, definisi formal / penjelasan tentang cakupan kode, dll.

UPDATE: Juga, saya tidak yakin, secara intuitif, tentang hubungan antara verifikasi formal dan apa yang saya minta, tetapi jelas ada semacam koneksi.

Erik Kaplun
sumber
1
Pengujian perangkat lunak sangat berharga (misalnya mempraktikkan pengujian unit) tetapi teorinya akan selalu memiliki beberapa lubang. Pertimbangkan contoh klasik ini: double pihole(double value) { return (value - Math.PI) / (value - Math.PI); }yang saya pelajari dari guru matematika saya . Kode ini memiliki satu lubang , yang tidak dapat ditemukan secara otomatis hanya dari pengujian kotak hitam. Dalam Matematika tidak ada lubang seperti itu. Dalam kalkulus Anda diizinkan menutup lubang jika batas satu sisi sama.
rwong
4
Ini mungkin bagian dari apa yang Anda cari - en.wikipedia.org/wiki/Formal_verification
enderland
1
Saya saran kedua @ enderland. Tidak masalah seberapa ketat pendekatan Anda dalam pengujian; beberapa bug masih akan lolos, dan saat Anda menutupi lebih banyak kode dengan tes Anda, biaya untuk menemukan bug baru naik. Ini mungkin mengapa tidak ada yang mengalami kesulitan memformalkan gagasan pengujian - pendekatan "heuristik" bekerja hampir sama dengan pelatihan yang lebih sedikit.
Doval
Sejak itu saya telah membiasakan diri dengan tanah verifikasi formal melalui tipe dependen, dan saya sepenuhnya setuju dengan @Doval dan enderland.
Erik Kaplun
1
@ rwong Saya kira Anda menyinggung kemungkinan baik pembilang dan penyebut sama dengan nol. Sebagian masalahnya karena desain yang buruk dari fungsi ini. Saat berbicara tentang verifikasi formal matematis, Anda perlu membuat fungsi Anda tidak sembarangan tetapi mengikuti aturan formal, berdasarkan tipe data yang benar. Dalam contoh ini Anda harus menggunakan fungsi pembagian (a,b)=>a/b, yang perlu diperluas dengan nilai melimpah, agar dapat dikomposasikan dengan benar.
Dmitri Zaitsev

Jawaban:

5

Saya tidak dapat menunjuk ke sumber daring yang bagus (artikel Wikipedia bahasa Inggris tentang topik ini cenderung tidak dapat diperbaiki), tetapi saya dapat merangkum ceramah yang saya dengar yang juga membahas teori pengujian dasar.

Mode pengujian

Ada beberapa kelas tes, seperti tes unit atau tes integrasi . Tes unit menyatakan bahwa sepotong kode yang koheren (fungsi, kelas, modul) diambil sesuai dengan fungsinya sendiri, sedangkan tes integrasi menyatakan bahwa beberapa bagian tersebut bekerja dengan benar bersama-sama.

Sebuah test case adalah lingkungan yang dikenal di mana sepotong kode dieksekusi, misalnya dengan menggunakan input tes khusus, atau dengan mengejek kelas lain. Perilaku kode kemudian dibandingkan dengan perilaku yang diharapkan, misalnya nilai pengembalian spesifik.

Sebuah tes hanya dapat membuktikan adanya bug, tidak pernah tidak adanya semua bug. Tes menempatkan batas atas kebenaran program.

Cakupan Kode

Untuk menentukan metrik cakupan kode, kode sumber dapat diterjemahkan ke grafik aliran kontrol di mana setiap node berisi segmen linier dari kode. Kontrol mengalir antara node-node ini hanya di akhir setiap blok, dan selalu bersyarat (jika kondisinya, maka goto node A, kalau tidak goto node B). Grafik memiliki satu simpul mulai dan satu simpul ujung.

  • Dengan grafik ini, cakupan pernyataan adalah rasio dari semua node yang dikunjungi ke semua node. Cakupan pernyataan lengkap tidak cukup untuk pengujian menyeluruh.
  • Cakupan cabang adalah rasio dari semua tepi yang dikunjungi antara node dalam CFG dengan semua tepi. Ini tidak cukup menguji loop.
  • Cakupan jalur adalah rasio dari semua jalur yang dikunjungi ke semua jalur, di mana jalur adalah urutan tepi mana pun dari awal hingga akhir. Masalahnya adalah bahwa dengan loop, mungkin ada jumlah lintasan yang tidak terbatas, sehingga cakupan lintasan penuh tidak dapat diuji secara praktis.

Oleh karena itu seringkali berguna untuk memeriksa cakupan kondisi .

  • Dalam cakupan kondisi sederhana , setiap kondisi atom pernah benar dan salah sekali - tetapi ini tidak menjamin cakupan pernyataan penuh.
  • Dalam cakupan beberapa kondisi , kondisi atom telah mengambil semua kombinasi truedan false. Ini menyiratkan cakupan cabang penuh, tetapi agak mahal. Program mungkin memiliki kendala tambahan yang mengecualikan kombinasi tertentu. Teknik ini baik untuk mendapatkan cakupan cabang, dapat menemukan kode mati, tetapi tidak dapat menemukan bug yang berasal dari kondisi yang salah .
  • Dalam cakupan beberapa kondisi Minimal , setiap kondisi atomik dan komposit dulunya benar dan salah. Itu masih menyiratkan cakupan cabang penuh. Ini adalah bagian dari cakupan beberapa kondisi, tetapi membutuhkan lebih sedikit kasus uji.

Saat membuat input uji menggunakan cakupan kondisi, maka hubungan arus pendek harus diperhitungkan. Sebagai contoh,

function foo(A, B) {
  if (A && B) x()
  else        y()
}

kebutuhan untuk diuji dengan foo(false, whatever), foo(true, false), dan foo(true, true)untuk cakupan kondisi beberapa minim penuh.

Jika Anda memiliki objek yang dapat berada dalam beberapa status, maka pengujian semua transisi status yang dianalogikan untuk mengontrol aliran tampaknya masuk akal.

Ada beberapa metrik cakupan yang lebih kompleks, tetapi umumnya mirip dengan metrik yang disajikan di sini.

Ini adalah metode pengujian kotak putih , dan sebagian dapat diotomatisasi. Perhatikan bahwa rangkaian uji unit harus bertujuan untuk memiliki cakupan kode tinggi dengan metrik apa pun yang dipilih, tetapi 100% tidak selalu mungkin. Terutama sulit untuk menguji penanganan pengecualian, di mana kesalahan harus disuntikkan ke lokasi tertentu.

Tes fungsional

Lalu ada tes fungsional yang menyatakan bahwa kode mematuhi spesifikasi dengan melihat implementasi sebagai kotak hitam. Tes semacam itu berguna untuk pengujian unit dan pengujian integrasi. Karena tidak mungkin untuk menguji dengan semua data input yang mungkin (misalnya menguji panjang string dengan semua string yang mungkin), maka berguna untuk mengelompokkan input (dan output) ke dalam kelas yang setara - jika length("foo")benar, foo("bar")kemungkinan akan bekerja juga. Untuk setiap kemungkinan kombinasi antara input dan output kelas kesetaraan, setidaknya satu input representatif dipilih dan diuji.

Satu juga harus menguji

  • kasus tepi length(""), foo("x"), length(longer_than_INT_MAX),
  • nilai-nilai yang diizinkan oleh bahasa, tetapi tidak oleh kontrak fungsi length(null), dan
  • kemungkinan data sampah length("null byte in \x00 the middle")...

Dengan angka, ini berarti pengujian 0, ±1, ±x, MAX, MIN, ±∞, NaN, dan dengan perbandingan titik mengambang menguji dua pelampung yang berdekatan. Sebagai tambahan lain, nilai tes acak dapat diambil dari kelas ekivalensi. Untuk memudahkan debugging, ada baiknya merekam seed yang digunakan ...

Tes non-fungsional: Load test, Stress test

Sepotong perangkat lunak memiliki persyaratan non-fungsional, yang harus diuji juga. Ini termasuk pengujian pada batas yang ditentukan (tes beban), dan di luar batas itu (tes stres). Untuk gim komputer, ini bisa berarti jumlah minimum frame per detik dalam uji beban. Sebuah situs web mungkin diuji stres untuk mengamati waktu respons ketika pengunjung dua kali lebih banyak dari yang diperkirakan menghancurkan server. Tes semacam itu tidak hanya relevan untuk seluruh sistem tetapi juga untuk entitas tunggal - bagaimana tabel hash terdegradasi dengan sejuta entri?

Jenis pengujian lainnya adalah pengujian sistem keseluruhan di mana skenario disimulasikan, atau tes penerimaan untuk membuktikan bahwa kontrak pengembangan telah dipenuhi.

Metode non-pengujian

Ulasan

Ada teknik non-pengujian yang dapat digunakan untuk jaminan kualitas. Contohnya adalah penelusuran, tinjauan kode formal, atau pemrograman pasangan. Sementara beberapa bagian dapat diotomatisasi (misalnya dengan menggunakan linter), ini umumnya memakan waktu. Namun, tinjauan kode oleh programmer berpengalaman memiliki tingkat penemuan bug yang tinggi, dan sangat berharga selama desain, di mana tidak ada pengujian otomatis yang memungkinkan.

Ketika ulasan kode sangat bagus, mengapa kita masih menulis tes? Keuntungan besar dari test suites adalah mereka dapat berjalan (kebanyakan) secara otomatis, dan sangat berguna untuk tes regresi .

Verifikasi formal

Verifikasi formal berjalan dan membuktikan sifat-sifat tertentu dari kode. Verifikasi manual sebagian besar layak untuk bagian-bagian penting, kurang untuk seluruh program. Bukti menempatkan batas bawah pada kebenaran program. Bukti dapat diotomatisasi ke tingkat tertentu, misalnya melalui pemeriksa tipe statis.

Invarian tertentu dapat secara eksplisit diperiksa dengan menggunakan assertpernyataan.


Semua teknik ini memiliki tempat masing-masing, dan saling melengkapi. TDD menulis tes fungsional di depan, tetapi tes dapat dinilai dengan metrik cakupan setelah kode diterapkan.

Menulis kode yang dapat diuji berarti menulis unit kode kecil yang dapat diuji secara terpisah (fungsi pembantu dengan rincian yang sesuai, prinsip tanggung jawab tunggal). Semakin sedikit argumen yang dilakukan masing-masing fungsi, semakin baik. Kode tersebut juga cocok untuk penyisipan objek tiruan, misalnya melalui injeksi ketergantungan.

amon
sumber
2
Saya menghargai jawaban yang rumit, tapi aku takut itu hampir tidak ada hubungannya dengan apa yang saya minta :) Tapi, untungnya, programmers.stackexchange.com/questions/78675/... tampaknya sedekat itu sampai ke apa yang saya bertujuan.
Erik Kaplun
Ini barang bagus. Bisakah Anda merekomendasikan buku atau benda apa saja?
Marcin
4

Mungkin "pengujian berbasis spesifikasi" juga menjawab pertanyaan Anda. Periksa Modul Pengujian ini (yang belum saya gunakan). Mereka mengharuskan Anda untuk menulis ekspresi matematika untuk menentukan set nilai tes, daripada menulis tes unit menggunakan nilai data tunggal yang dipilih.

Tes :: Lectrotest

Seperti yang dikatakan penulis, Modul Perl ini terinspirasi oleh Modul Pemeriksaan Cepat Haskell . Ada lebih banyak tautan di halaman ini, beberapa di antaranya sudah mati.

knb
sumber
2

Salah satu pendekatan berbasis matematis adalah pengujian semua pasangan . Idenya adalah bahwa sebagian besar bug diaktifkan oleh pilihan opsi konfigurasi tunggal, dan sebagian besar sisanya diaktifkan oleh sepasang opsi tertentu yang diambil secara bersamaan. Dengan demikian sebagian besar dapat ditangkap dengan menguji "semua pasangan". Penjelasan matematis (dengan generalisasi) ada di sini:

Sistem AETG: pendekatan untuk pengujian berdasarkan pada desain kombinatorial

(ada lebih banyak referensi seperti itu)

David Ketcheson
sumber
2

Ada beberapa persamaan matematika yang digunakan, tetapi itu tergantung pada jenis pengujian perangkat lunak yang Anda gunakan. Sebagai contoh, Critical Fault Assumption mengasumsikan bahwa kegagalan bukanlah hasil dari 2 atau lebih kesalahan simultan. Persamaan berikut adalah: f = 4n + 1. f = fungsi yang menghitung jumlah kasus uji untuk sejumlah variabel tertentu ( n) + 1 adalah penambahan konstanta di mana semua variabel mengasumsikan nilai nominal.

Tipe lain dari pengujian yang membutuhkan persamaan matematika adalah Robustness Testing adalah pengujian ketahanan, atau kebenaran kasus uji dalam proses pengujian. Dalam tes ini, Anda akan memasukkan variabel dalam rentang input yang sah (kasus uji bersih) dan variabel input di luar rentang input (kasus uji kotor). Anda akan menggunakan persamaan matematika berikut: f = 6n +1 . 6n menunjukkan bahwa masing-masing variabel harus mengasumsikan 6 nilai yang berbeda sedangkan nilai lainnya mengasumsikan nilai nominal. * + 1 * mewakili penambahan konstanta 1.

Charles Lucas Shabazz
sumber