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:
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.