Unit-testing algoritma inheren acak / non-deterministik

41

Proyek saya saat ini, secara ringkas, melibatkan penciptaan "kejadian acak yang terbatas". Saya pada dasarnya membuat jadwal inspeksi. Beberapa dari mereka didasarkan pada batasan jadwal yang ketat; Anda melakukan inspeksi sekali seminggu pada hari Jumat pukul 10:00 pagi. Inspeksi lain adalah "acak"; ada persyaratan dasar yang dapat dikonfigurasi seperti "inspeksi harus dilakukan 3 kali per minggu", "inspeksi harus terjadi antara jam 9 pagi - 9 malam", dan "tidak boleh ada dua inspeksi dalam periode 8 jam yang sama", tetapi dalam batasan apa pun yang dikonfigurasikan untuk serangkaian inspeksi tertentu, tanggal dan waktu yang dihasilkan tidak dapat diprediksi.

Tes unit dan TDD, IMO, memiliki nilai besar dalam sistem ini karena dapat digunakan untuk membangunnya secara bertahap sementara persyaratan lengkapnya masih belum lengkap, dan pastikan saya tidak "melakukan rekayasa berlebihan" untuk melakukan hal-hal yang saya tidak lakukan. saat ini tahu saya perlu. Jadwal yang ketat adalah sepotong kue untuk TDD. Namun, saya merasa sulit untuk benar-benar mendefinisikan apa yang saya uji ketika saya menulis tes untuk bagian acak dari sistem. Saya dapat menyatakan bahwa semua waktu yang dihasilkan oleh penjadwal harus berada dalam batasan, tetapi saya bisa menerapkan algoritma yang melewati semua tes tersebut tanpa waktu yang sebenarnya sangat "acak". Sebenarnya itulah yang terjadi; Saya menemukan masalah di mana waktu, meskipun tidak dapat diprediksi dengan tepat, jatuh ke bagian kecil dari rentang tanggal / waktu yang diijinkan. Algoritma masih melewati semua pernyataan yang saya rasa bisa saya buat secara masuk akal, dan saya tidak bisa merancang tes otomatis yang akan gagal dalam situasi itu, tetapi lulus ketika diberi hasil "lebih acak". Saya harus menunjukkan masalah diselesaikan dengan merestrukturisasi beberapa tes yang ada untuk mengulangi diri mereka beberapa kali, dan secara visual memeriksa bahwa waktu yang dihasilkan berada dalam kisaran penuh yang diijinkan.

Adakah yang punya tips untuk merancang tes yang seharusnya mengharapkan perilaku non-deterministik?


Terima kasih untuk semua sarannya. Pendapat utama tampaknya adalah bahwa saya memerlukan tes deterministik untuk mendapatkan hasil yang deterministik, berulang, tegas . Masuk akal.

Saya membuat satu set tes "kotak pasir" yang berisi kandidat algoritma untuk proses pembatas (proses dimana array byte yang bisa panjang menjadi panjang antara min dan max). Saya kemudian menjalankan kode itu melalui loop FOR yang memberikan algoritma beberapa byte array yang dikenal (nilai dari 1 hingga 10.000.000 hanya untuk memulai) dan memiliki algoritma membatasi masing-masing ke nilai antara 1009 dan 7919 (Saya menggunakan bilangan prima untuk memastikan Algoritma tidak akan melewati beberapa GCF kebetulan antara rentang input dan output). Nilai-nilai terbatas yang dihasilkan dihitung dan histogram dihasilkan. Untuk "lulus", semua input harus tercermin dalam histogram (kewarasan untuk memastikan kami tidak "kehilangan" apa pun), dan perbedaan antara dua ember dalam histogram tidak boleh lebih besar dari 2 (harus benar-benar <= 1 , tapi tetap disini). Algoritma pemenang, jika ada, dapat dipotong dan disisipkan langsung ke dalam kode produksi dan tes permanen dilakukan untuk regresi.

Ini kodenya:

    private void TestConstraintAlgorithm(int min, int max, Func<byte[], long, long, long> constraintAlgorithm)
    {
        var histogram = new int[max-min+1];
        for (int i = 1; i <= 10000000; i++)
        {
            //This is the stand-in for the PRNG; produces a known byte array
            var buffer = BitConverter.GetBytes((long)i);

            long result = constraintAlgorithm(buffer, min, max);

            histogram[result - min]++;
        }

        var minCount = -1;
        var maxCount = -1;
        var total = 0;
        for (int i = 0; i < histogram.Length; i++)
        {
            Console.WriteLine("{0}: {1}".FormatWith(i + min, histogram[i]));
            if (minCount == -1 || minCount > histogram[i])
                minCount = histogram[i];
            if (maxCount == -1 || maxCount < histogram[i])
                maxCount = histogram[i];
            total += histogram[i];
        }

        Assert.AreEqual(10000000, total);
        Assert.LessOrEqual(maxCount - minCount, 2);
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionMSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByMSBRejection);
    }

    private long ConstrainByMSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length-1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;
        //Apply a bitmask to the value, removing the MSB on each loop until it falls in the range.
        var mask = long.MaxValue;
        while (result > max - min)
        {
            mask >>= 1;
            result &= mask;
        }
        result += min;

        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionLSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByLSBRejection);
    }

    private long ConstrainByLSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length - 1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;

        //Bit-shift the number 1 place to the right until it falls within the range
        while (result > max - min)
            result >>= 1;

        result += min;
        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionModulus()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByModulo);
    }

    private long ConstrainByModulo(byte[] buffer, long min, long max)
    {
        buffer[buffer.Length - 1] &= 0x7f;
        var result = BitConverter.ToInt64(buffer, 0);

        //Modulo divide the value by the range to produce a value that falls within it.
        result %= max - min + 1;

        result += min;
        return result;
    }

... dan inilah hasilnya:

masukkan deskripsi gambar di sini

Penolakan LSB (menggeser-geser angkanya sampai masuk dalam kisaran) adalah TERRIBLE, untuk alasan yang sangat mudah untuk dijelaskan; ketika Anda membagi angka dengan 2 sampai kurang dari maksimum, Anda berhenti segera setelah itu, dan untuk rentang non-sepele, yang akan membiaskan hasil menuju sepertiga atas (seperti yang terlihat dalam hasil rinci histogram ). Ini persis perilaku yang saya lihat dari tanggal selesai; semua waktu di sore hari, pada hari-hari yang sangat spesifik.

Penolakan MSB (menghapus bit paling signifikan dari nomor satu pada satu waktu sampai berada dalam kisaran) lebih baik, tetapi sekali lagi, karena Anda memotong angka yang sangat besar dengan setiap bit, itu tidak terdistribusi secara merata; Anda tidak mungkin mendapatkan angka di ujung atas dan bawah, sehingga Anda bias ke sepertiga tengah. Itu mungkin menguntungkan seseorang yang ingin "menormalkan" data acak menjadi kurva lonceng, tetapi jumlah dua atau lebih angka acak yang lebih kecil (mirip dengan melempar dadu) akan memberi Anda kurva yang lebih alami. Demi tujuan saya, itu gagal.

Satu-satunya yang lulus tes ini adalah dibatasi oleh divisi modulo, yang juga ternyata yang tercepat dari ketiganya. Modulo akan, menurut definisinya, menghasilkan distribusi sebanyak mungkin dengan input yang tersedia.

KeithS
sumber
2
Jadi, pada akhirnya, Anda ingin sebuah program yang melihat output dari generator angka acak dan memutuskan apakah itu acak? Seperti dalam "5,4,10,31,120,390,2,3,4" adalah acak tetapi "49,39,1,10,103,12,4,189" bukan?
psr
Tidak, tetapi menghindari memperkenalkan bias antara PRNG yang sebenarnya dan hasil akhirnya akan menyenangkan.
KeithS
Maka kedengarannya seperti mengejek PRNG akan baik-baik saja. Anda tidak perlu nilai acak aktual untuk menguji bahwa Anda tidak memotong nilai. Jika Anda memiliki bug yang meremas nilai acak ke dalam subset rentang yang diijinkan yang terlalu kecil, Anda pasti mendapatkan beberapa nilai spesifik yang salah.
psr
Anda juga harus menguji kombinasi. Memiliki inspeksi yang diperkirakan setara per jam tidak akan mencegah kasus di mana, katakanlah, inspeksi pukul 11 ​​pagi pada hari Selasa selalu diikuti oleh pukul 14:00 pada hari Kamis dan pukul 10 pagi pada hari Jumat.
David Thornley
Itu lebih merupakan ujian PRNG itu sendiri; uji mekanisme pembatas (s), seperti terstruktur di atas, akan selalu gagal uji seperti itu karena diberikan set data yang benar-benar non-acak. Dengan asumsi mekanisme kendala tidak berusaha untuk "memesan" data acak yang saya sebut "pengujian eksternal" yang merupakan sesuatu yang tidak boleh dilakukan oleh unit test.
KeithS

Jawaban:

17

Apa yang sebenarnya ingin Anda uji di sini, saya asumsikan, adalah karena diberikan serangkaian hasil spesifik dari pengacak, sisa metode Anda berkinerja dengan benar.

Jika itu yang Anda cari maka tirulah pengacak, untuk membuatnya deterministik dalam bidang pengujian.

Saya biasanya memiliki objek tiruan untuk semua jenis data yang tidak dapat ditentukan atau tidak dapat diprediksi (pada saat penulisan tes), termasuk generator GUID dan DateTime. Sekarang.

Edit, dari komentar: Anda harus mengejek PRNG (istilah itu lolos dari saya tadi malam) pada tingkat serendah mungkin - yaitu. ketika itu menghasilkan array byte, bukan setelah Anda mengubahnya menjadi Int64s. Atau bahkan pada kedua level, sehingga Anda dapat menguji konversi Anda ke array Int64 berfungsi sebagaimana dimaksud dan kemudian menguji secara terpisah bahwa konversi Anda ke array DateTimes berfungsi sebagaimana dimaksud. Seperti yang dikatakan Jonathon, Anda bisa melakukannya dengan memberikan set seed, atau Anda dapat memberikan array byte untuk dikembalikan.

Saya lebih suka yang terakhir karena tidak akan rusak jika implementasi kerangka kerja PRNG berubah. Namun, satu keuntungan untuk memberikannya benih adalah bahwa jika Anda menemukan sebuah kasus dalam produksi yang tidak berfungsi sebagaimana dimaksud, Anda hanya perlu mencatat satu nomor untuk dapat menggandakannya, sebagai lawan dari keseluruhan susunan.

Semua ini mengatakan, Anda harus ingat bahwa itu disebut Pseudo Random Number Generator karena suatu alasan. Mungkin ada beberapa bias bahkan pada level itu.

pdr
sumber
1
Tidak. Yang ingin saya uji dalam kasus ini adalah pengacak itu sendiri, dan menyatakan bahwa nilai "acak" yang dihasilkan oleh pengacak jatuh dalam batasan yang ditentukan sementara masih "acak", karena tidak bias terhadap distribusi yang tidak merata di seluruh yang diijinkan rentang waktu. Saya dapat dan secara pasti menguji bahwa tanggal / waktu tertentu dengan benar melewati atau gagal mengatasi kendala tertentu, tetapi masalah sebenarnya yang saya temui adalah bahwa tanggal yang dihasilkan oleh pengacak bias dan karenanya dapat diprediksi.
KeithS
Satu-satunya hal yang dapat saya pikirkan adalah membuat randomizer memuntahkan banyak tanggal dan membuat histogram, kemudian menyatakan bahwa nilainya terdistribusi secara relatif merata. Itu tampaknya sangat berat, dan masih belum deterministik karena setiap set data yang benar-benar acak dapat menunjukkan bias yang jelas bahwa set yang lebih besar kemudian akan membantah.
KeithS
1
Itu adalah ujian yang akan pecah sesekali dan tak terduga. Anda tidak menginginkan itu. Saya pikir Anda salah paham maksud saya, jujur ​​saja. Di suatu tempat di dalam apa yang Anda sebut pengacak, harus ada garis kode yang menghasilkan angka acak, bukan? Baris itu adalah apa yang saya sebut sebagai pengacak, dan sisa dari apa yang Anda sebut pengacak (distribusi tanggal berdasarkan data "acak") adalah apa yang ingin Anda uji. Atau apakah saya melewatkan sesuatu?
pdr
metrik statistik yang benar dari urutan acak (korelasi, blok-korelasi, rata-rata, standar deviasi, dll.) hanya akan gagal memenuhi rentang harapan Anda hanya jika Anda melakukan sampel yang sangat kecil. Tingkatkan set sampel Anda dan / atau tingkatkan bilah kesalahan yang diizinkan
lurscher
1
"beberapa bias bahkan pada tingkat itu" Jika Anda menggunakan PRNG yang baik, maka Anda tidak akan dapat menemukan tes apa pun (dengan batas komputasi realistis) yang dapat membedakannya dari keacakan nyata. Jadi dalam praktiknya orang dapat berasumsi bahwa PRNG yang baik tidak memiliki bias apa pun.
CodesInChaos
23

Ini akan terdengar seperti jawaban bodoh, tapi saya akan membuangnya di sana karena ini adalah bagaimana saya melihatnya dilakukan sebelumnya:

Pisahkan kode Anda dari PRNG - berikan benih pengacakan ke semua kode yang menggunakan pengacakan. Kemudian Anda dapat menentukan nilai 'bekerja' dari satu biji (atau beberapa biji yang akan membuat Anda merasa lebih baik). Ini akan memberi Anda kemampuan untuk menguji kode Anda secara memadai tanpa harus bergantung pada hukum jumlah besar.

Kedengarannya gila, tetapi beginilah cara militer melakukannya (entah itu atau mereka menggunakan 'tabel acak' yang sama sekali tidak acak)

Jonathan Rich
sumber
Tepat: jika Anda tidak dapat menguji elemen dari suatu algoritma, abstraksi dan tirukannya
Steve Greatrex
Saya tidak menentukan nilai benih deterministik; sebagai gantinya, saya menghapus elemen "acak" seluruhnya sehingga saya bahkan tidak harus bergantung pada algoritma PRNG tertentu. Saya dapat menguji itu, mengingat sejumlah besar angka-angka yang terdistribusi secara merata, algoritma yang saya gunakan dapat membatasi mereka ke kisaran yang lebih kecil tanpa menimbulkan bias. PRNG itu sendiri harus diuji secara memadai oleh siapa pun yang mengembangkannya (saya menggunakan RNGCryptoServiceProvider).
KeithS
Mengenai pendekatan "tabel acak", Anda juga dapat menggunakan uji implementasi yang berisi algoritme penghasil angka "reversibel". Ini memungkinkan Anda untuk "memundurkan" PRNG atau bahkan meminta untuk melihat apa output N terakhir itu. Ini akan memungkinkan lebih banyak debugging mendalam dalam skenario tertentu.
Darien
Ini tidak sebodoh itu - ini adalah metode yang sama yang digunakan Google untuk mereproduksi kegagalan injeksi dalam tes Spanner menurut makalah mereka :)
Akshat Mahajan
6

"Apakah ini acak (cukup)" ternyata menjadi pertanyaan yang sangat halus. Jawaban singkatnya adalah bahwa tes unit tradisional tidak akan memotongnya - Anda harus menghasilkan banyak nilai acak dan mengirimkannya ke berbagai tes statistik yang memberi Anda kepercayaan tinggi bahwa mereka cukup acak untuk kebutuhan Anda.

Akan ada sebuah pola - kita menggunakan generator nomor psuedo-random. Tetapi pada titik tertentu hal-hal akan menjadi "cukup baik" untuk aplikasi Anda (di mana cukup banyak BANYAK bervariasi antara katakan game di satu ujung, di mana generator yang relatif sederhana sudah cukup, semua jalan sampai ke kriptografi di mana Anda benar-benar perlu urutan yang tidak layak untuk menentukan oleh penyerang yang gigih dan lengkap).

Artikel Wikipedia http://en.wikipedia.org/wiki/Randomness_tests dan tautan tindak lanjutnya memiliki informasi lebih lanjut.

James Iry
sumber
Bahkan PRNG biasa-biasa saja tidak akan menunjukkan patters dalam tes statistik apa pun. Untuk PRNG yang baik praktis tidak mungkin untuk membedakannya dari angka acak nyata.
CodesInChaos
4

Saya punya dua jawaban untuk Anda.

=== JAWABAN PERTAMA ===

Segera setelah saya melihat judul pertanyaan Anda, saya datang untuk melompat dan mengusulkan solusinya. Solusi saya sama dengan apa yang diusulkan beberapa orang lain: untuk mengejek generator nomor acak Anda. Lagi pula, saya telah membangun beberapa program berbeda yang memerlukan trik ini untuk menulis tes unit yang baik, dan saya mulai membuat akses yang dapat dipermainkan ke angka acak sebagai praktik standar dalam semua pengkodean saya.

Tapi kemudian saya membaca pertanyaan Anda. Dan untuk masalah khusus yang Anda gambarkan, itu bukan jawabannya. Masalah Anda bukan karena Anda perlu membuat proses yang dapat diprediksi menggunakan angka acak (jadi itu bisa diuji). Sebaliknya, masalah Anda adalah untuk memverifikasi bahwa algoritma Anda memetakan output acak seragam dari RNG Anda ke output uniform-dalam-kendala dari algoritma Anda - bahwa jika RNG yang mendasarinya seragam maka akan menghasilkan waktu inspeksi yang terdistribusi secara merata (tergantung pada kendala masalah).

Itu masalah yang sangat sulit (tapi cukup jelas). Yang berarti ini adalah masalah MENARIK. Saya segera mulai memikirkan beberapa ide hebat untuk menyelesaikannya. Kembali ketika saya adalah seorang programmer jagoan saya mungkin sudah mulai melakukan sesuatu dengan ide-ide ini. Tapi saya bukan programmer jagoan lagi ... Saya suka bahwa saya lebih berpengalaman dan lebih terampil sekarang.

Jadi alih-alih menyelam ke masalah yang sulit, saya berpikir dalam hati: apa nilai dari ini? Dan jawabannya mengecewakan. Bug Anda sudah dipecahkan, dan Anda akan rajin dengan masalah ini di masa depan. Keadaan eksternal tidak dapat memicu masalah, hanya perubahan pada algoritma Anda. Satu-satunya alasan untuk mengatasi masalah yang menarik ini adalah untuk memenuhi praktik TDD (Test Driven Design). Jika ada satu hal yang saya pelajari adalah bahwa dengan membabi buta mengikuti praktik apa pun saat tidak bernilai menyebabkan masalah. Saran saya adalah ini: Hanya saja jangan menulis tes untuk ini, dan lanjutkan.


=== JAWABAN KEDUA ===

Wow ... masalah yang sangat keren!

Yang perlu Anda lakukan di sini adalah menulis tes yang memverifikasi bahwa algoritma Anda untuk memilih tanggal dan waktu inspeksi akan menghasilkan output yang terdistribusi secara seragam (dalam kendala masalah) jika RNG yang digunakannya menghasilkan angka yang terdistribusi secara merata. Berikut adalah beberapa pendekatan, diurutkan berdasarkan tingkat kesulitan.

  1. Anda dapat menerapkan brute force. Jalankan algoritma beberapa kali saja, dengan RNG nyata sebagai input. Periksa hasil keluaran untuk melihat apakah hasilnya terdistribusi secara merata. Tes Anda harus gagal jika distribusi bervariasi dari seragam sempurna dengan lebih dari ambang batas tertentu, dan untuk memastikan Anda menangkap masalah ambang batas tidak dapat disetel terlalu rendah. Itu berarti bahwa Anda akan memerlukan sejumlah besar menjalankan untuk memastikan bahwa probabilitas positif palsu (kegagalan uji secara acak) sangat kecil (baik <1% untuk basis kode berukuran sedang; bahkan lebih sedikit untuk basis kode besar).

  2. Pertimbangkan algoritma Anda sebagai fungsi yang mengambil rangkaian semua output RNG sebagai input, lalu menghasilkan waktu inspeksi sebagai output. Jika Anda tahu bahwa fungsi ini terus menerus, maka ada cara untuk menguji properti Anda. Ganti RNG dengan RNG yang dapat diolok-olok dan jalankan algoritma beberapa kali, menghasilkan output RNG yang terdistribusi secara merata. Jadi, jika kode Anda membutuhkan 2 panggilan RNG, masing-masing dalam kisaran [0..1], Anda mungkin harus menjalankan algoritme sebanyak 100 kali, mengembalikan nilai [(0,0,0.0), (0,0,0.1), (0,0, 0.2), ... (0.0.0.9), (0.1.0.0), (0.1.0.1), ... (0.9.0.9)]. Kemudian Anda dapat memeriksa apakah output 100 berjalan (kurang-lebih) terdistribusi secara merata dalam rentang yang diizinkan.

  3. Jika Anda BENAR-BENAR perlu memverifikasi algoritma dengan cara yang dapat diandalkan dan Anda tidak dapat membuat asumsi tentang algoritma ATAU menjalankan sejumlah besar kali, maka Anda masih dapat menyerang masalah, tetapi Anda mungkin perlu beberapa kendala tentang bagaimana Anda memprogram algoritma . Lihat PyPy dan pendekatan Object Space mereka sebagai contoh. Anda bisa membuat Object Space yang, bukannya benar-benar mengeksekusi algoritma, alih-alih hanya menghitung bentuk distribusi output (dengan asumsi bahwa input RNG seragam). Tentu saja, ini mengharuskan Anda membuat alat seperti itu dan algoritma Anda dibuat di PyPy atau alat lain di mana mudah untuk membuat modifikasi drastis ke kompiler dan menggunakannya untuk menganalisis kode.

mcherm
sumber
3

Untuk pengujian unit, ganti generator acak dengan kelas yang menghasilkan hasil yang dapat diprediksi yang mencakup semua kasing sudut . Yaitu memastikan pseudo-randomizer Anda menghasilkan nilai serendah mungkin dan nilai setinggi mungkin, dan hasil yang sama beberapa kali berturut-turut.

Anda tidak ingin unit test Anda diabaikan misalnya bug off-by-one terjadi ketika Random.nextInt (1000) mengembalikan 0 atau 999.

pengguna281377
sumber
3

Anda mungkin ingin melihat Sevcikova et al: "Pengujian Otomatis Sistem Stochastic: Suatu Pendekatan yang Dibumi Secara Statistik" ( PDF ).

Metodologi ini diimplementasikan dalam berbagai uji kasus untuk platform simulasi UrbanSim .

krlmlr
sumber
Itu bagus, di sana.
KeithS
2

Pendekatan histogram sederhana adalah langkah pertama yang baik, tetapi tidak cukup untuk membuktikan keacakan. Untuk PRNG yang seragam Anda juga akan (paling tidak) menghasilkan sebar plot 2 dimensi (di mana x adalah nilai sebelumnya dan y adalah nilai baru). Plot ini juga harus seragam. Ini rumit dalam situasi Anda karena ada non-linearitas yang disengaja dalam sistem.

Pendekatan saya adalah:

  1. memvalidasi (atau menganggap bahwa sumber) PRNG cukup acak (menggunakan ukuran statistik standar)
  2. memverifikasi bahwa konversi PRNG-ke-datetime yang tidak dibatasi cukup acak atas ruang output (ini memverifikasi kurangnya bias dalam konversi). Tes keseragaman tingkat pertama yang sederhana harus memadai di sini.
  3. memverifikasi bahwa kas yang dibatasi cukup seragam (tes keseragaman orde pertama yang sederhana di atas nampan yang valid).

Setiap tes ini bersifat statistik dan membutuhkan sejumlah besar titik sampel untuk menghindari positif palsu dan negatif palsu dengan tingkat kepercayaan yang tinggi.

Adapun sifat dari algoritma konversi / kendala:

Diberikan: metode untuk menghasilkan nilai pseudo-acak p di mana 0 <= p <= M

Need: output y dalam (mungkin diskontinu) kisaran 0 <= y <= N <= M

Algoritma:

  1. menghitung r = floor(M / N), yaitu, jumlah rentang output lengkap yang sesuai dengan rentang input.
  2. hitung nilai maks yang dapat diterima untuk p :p_max = r * N
  3. menghasilkan nilai untuk p sampai nilai kurang dari atau sama dengan p_maxditemukan
  4. menghitung y = p / r
  5. jika y dapat diterima, kembalikan, jika tidak ulangi dengan langkah 3

Kuncinya adalah membuang nilai yang tidak dapat diterima daripada melipat secara tidak seragam.

dalam pseudo-code:

# assume prng generates non-negative values
def randomInRange(min, max, prng):
    range = max - min
    factor = prng.max / range

    do:
        value = prng()
    while value > range * factor
    return (value / factor) + min

def constrainedRandom(constraint, prng):
    do:
        value = randomInRange(constraint.min, constraint.max, prng)
    while not constraint.is_acceptable(value)
Frank Szczerba
sumber
1

Selain memvalidasi bahwa kode Anda tidak gagal, atau melempar pengecualian tepat di tempat yang tepat, Anda dapat membuat pasangan input / respons yang valid (bahkan menghitung ini secara manual), masukkan input dalam tes dan pastikan itu mengembalikan respons yang diharapkan. Tidak hebat, tapi hanya itu yang bisa Anda lakukan, imho. Namun, dalam kasus Anda itu tidak benar-benar acak, setelah Anda membuat jadwal Anda, Anda dapat menguji kepatuhan aturan - harus memiliki 3 inspeksi per minggu, antara 9-9; tidak ada kebutuhan nyata atau kemampuan untuk menguji waktu yang tepat ketika inspeksi terjadi.

Evgeni
sumber
1

Benar-benar tidak ada cara yang lebih baik daripada menjalankannya beberapa kali dan melihat apakah Anda mendapatkan distribusi yang Anda inginkan. Jika Anda memiliki 50 jadwal inspeksi potensial yang diizinkan, Anda menjalankan tes 500 kali dan memastikan setiap jadwal digunakan hampir 10 kali. Anda dapat mengontrol benih generator acak Anda untuk membuatnya lebih deterministik, tetapi itu juga akan membuat pengujian Anda lebih erat dengan rincian implementasi.

Karl Bielefeldt
sumber
Tetapi jika itu benar-benar acak, maka kadang-kadang, beberapa jadwal tidak akan digunakan sama sekali; dan kadang-kadang, beberapa jadwal akan digunakan lebih dari 20 kali. Saya tidak tahu bagaimana Anda ingin menguji bahwa setiap jadwal digunakan "hampir 10 kali", tetapi kondisi apa pun yang Anda uji di sini, Anda akan memiliki tes yang terkadang gagal ketika program bekerja untuk menentukan.
Dawood mengatakan mengembalikan Monica
@ DawoodibnKareem dengan ukuran sampel yang cukup (dan batas keseragaman yang masuk akal) Anda dapat mengurangi peluang tes gagal menjadi 1 dalam miliaran. Dan umumnya statistik seperti itu eksponensial dengan n sehingga dibutuhkan kurang dari yang Anda harapkan untuk mendapatkan angka-angka itu.
mbrig
1

Tidak mungkin menguji kondisi samar-samar yang tidak memiliki definisi konkret. Jika tanggal yang dihasilkan lulus semua tes maka secara teoritis aplikasi Anda berfungsi dengan benar. Komputer tidak dapat memberi tahu Anda jika tanggalnya "cukup acak" karena tidak dapat mengakui kriteria untuk pengujian semacam itu. Jika semua tes lulus tetapi perilaku aplikasi masih tidak cocok maka cakupan tes Anda tidak memadai secara empiris (dari perspektif TDD).

Dalam pandangan saya, yang terbaik adalah menerapkan beberapa batasan pembuatan tanggal yang sewenang-wenang sehingga distribusinya lulus tes bau manusia.

Leo
sumber
2
Anda benar-benar dapat menentukan keacakan melalui pengujian otomatis. Anda cukup menghasilkan sampel dalam jumlah yang cukup besar dan menerapkan uji keacakan standar untuk mendeteksi bias dalam sistem. Ini adalah latihan pemrograman sarjana yang cukup standar.
Frank Szczerba
0

Catat saja output pengacak Anda (apakah pseudo atau kuantum / kacau atau dunia nyata). Kemudian simpan dan putar ulang urutan "acak" yang sesuai dengan persyaratan pengujian Anda, atau yang mengungkap potensi masalah dan bug, saat Anda membuat kotak uji unit Anda.

hotpaw2
sumber
0

Kasus ini tampaknya ideal untuk Pengujian Berbasis Properti .

Singkatnya, ini adalah mode pengujian di mana kerangka kerja pengujian menghasilkan input untuk kode yang sedang diuji dan pernyataan pengujian memvalidasi properti dari output. Kerangka kerja kemudian bisa cukup pintar untuk "menyerang" kode yang sedang diuji dan mencoba memojokkannya menjadi kesalahan. Kerangka kerja ini biasanya juga cukup pintar untuk membajak seed generator nomor acak Anda. Biasanya Anda dapat mengkonfigurasi kerangka kerja untuk menghasilkan paling banyak N test case atau menjalankan paling banyak N detik, dan ingat test case yang gagal dari run terakhir dan jalankan kembali ini terhadap versi kode yang lebih baru terlebih dahulu. Hal ini memungkinkan untuk siklus iterasi cepat selama pengembangan dan pengujian komprehensif lambat dari band / in CI.

Inilah contoh (bodoh, gagal) yang menguji sumfungsi:

@given(lists(floats()))
def test_sum(alist):
    result = sum(alist)
    assert isinstance(result, float)
    assert result > 0
  • kerangka uji akan menghasilkan satu daftar pada satu waktu
  • konten dari daftar ini adalah angka-angka floating point
  • sum dipanggil dan properti hasil divalidasi
  • hasilnya selalu mengambang
  • hasilnya positif

Tes ini akan menemukan banyak "bug" di sum(komentar jika Anda dapat menebak semua ini sendiri):

  • sum([]) is 0 (int, bukan float)
  • sum([-0.9]) negatif
  • sum([0.0]) tidak sepenuhnya positif
  • sum([..., nan]) is nan yang tidak positif

Dengan pengaturan default, hpythesisbatalkan tes setelah 1 input "buruk" ditemukan, yang bagus untuk TDD. Saya pikir itu mungkin untuk mengkonfigurasinya untuk melaporkan banyak / semua input "buruk", tetapi saya tidak dapat menemukan opsi itu sekarang.

Dalam kasus OP, properti yang divalidasi akan lebih kompleks: inspeksi tipe A sekarang, inspeksi tipe A tiga kali seminggu, waktu inspeksi B selalu pada pukul 12 siang, inspeksi tipe C dari 9 hingga 9, [jadwal yang diberikan selama satu minggu] inspeksi jenis A, B, C semuanya ada, dll.

Perpustakaan yang paling terkenal adalah QuickCheck for Haskell, lihat halaman Wikipedia di bawah ini untuk daftar perpustakaan seperti itu dalam bahasa lain:

https://en.wikipedia.org/wiki/QuickCheck

Hipotesis (untuk Python) memiliki artikel yang bagus tentang pengujian semacam ini:

https://hypothesis.works/articles/what-is-property-based-testing/

Dima Tisnek
sumber
-1

yang dapat Anda uji unit adalah logika menentukan apakah tanggal acak valid atau jika tanggal acak lain perlu dipilih.

Tidak ada cara untuk menguji generator tanggal acak pendek mendapatkan banyak tanggal dan memutuskan apakah mereka sesuai acak.

Ryathal
sumber
-1

Tujuan Anda bukan untuk menulis unit test dan lulus, tetapi untuk memastikan bahwa program Anda memenuhi persyaratannya. Satu-satunya cara Anda dapat melakukan ini, adalah dengan tepat mendefinisikan persyaratan Anda di tempat pertama. Misalnya, Anda menyebutkan "tiga inspeksi mingguan secara acak". Saya akan mengatakan persyaratannya adalah: (a) 3 inspeksi (bukan 2 atau 4), (b) pada waktu yang tidak dapat diprediksi oleh orang-orang yang tidak ingin diperiksa secara tak terduga, dan (c) tidak terlalu berdekatan - dua inspeksi yang terpisah lima menit mungkin tidak ada gunanya, mungkin juga tidak terlalu jauh.

Jadi, Anda menuliskan persyaratan lebih tepatnya daripada saya. (a) dan (c) mudah. Untuk (b), Anda dapat menulis beberapa kode sepintar mungkin yang mencoba memprediksi pemeriksaan selanjutnya, dan untuk lulus uji unit, kode itu tidak boleh dapat memprediksi lebih baik daripada perkiraan murni.

Dan tentu saja Anda perlu menyadari bahwa jika inspeksi Anda benar-benar acak, algoritma prediksi apa pun bisa benar secara kebetulan, jadi Anda harus yakin bahwa Anda dan unit test Anda tidak panik jika itu terjadi. Mungkin melakukan beberapa tes lagi. Saya tidak akan repot-repot menguji generator angka acak, karena pada akhirnya itu jadwal inspeksi yang diperhitungkan, dan tidak masalah bagaimana itu dibuat.

gnasher729
sumber
Tidak, tidak. Tes unit membuktikan program cocok dengan persyaratannya, sehingga keduanya adalah satu dan sama. Dan saya tidak dalam bisnis menulis perangkat lunak prediktif untuk merekayasa balik algoritma acak. Jika saya, saya tidak akan memberi tahu Anda tentang hal itu, saya akan membuat pembunuhan cracking situs web aman dengan memprediksi kunci mereka dan menjual rahasia kepada penawar tertinggi. Bisnis saya menulis penjadwal yang menciptakan waktu yang dapat dibatasi tetapi tidak dapat diprediksi dalam kendala, dan saya perlu tes deterministik untuk membuktikan saya telah melakukannya, bukan yang probabilistik yang mengatakan saya cukup yakin.
KeithS