Şu anki projem, kısaca, "sınırlanabilir-rastgele olayların" yaratılmasını içeriyor. Temel olarak bir teftiş programı oluşturuyorum. Bazıları katı program kısıtlamalarına dayanır; Cuma günleri saat 10: 00'da haftada bir kez inceleme yaparsınız. Diğer denetimler “rastgele”; "bir denetim haftada 3 kez yapılmalı", "muayene 9:00 - 9:00 arasında yapılmalı" ve "aynı 8 saatlik sürede iki denetim yapılmamalı" gibi temel yapılandırılabilir gereksinimler vardır, ancak Belirli bir denetleme kümesi için ne gibi kısıtlamalar yapıldıysa, ortaya çıkan tarih ve saatler öngörülebilir olmamalıdır.
Ünite testleri ve TDD, IMO, bu sistemde büyük bir değere sahiptir, çünkü tüm gereksinimler hala tamamlanmamışken, aşamalı olarak inşa etmek için kullanılabilir ve benim yaptığım işleri yapmak için "fazla mühendislik yapmadığımdan" emin olun şu anda ihtiyacım olduğunu bilmiyor. Kesin programlar TDD'ye bir parça kek oldu. Ancak, sistemin rastgele bölümü için testler yazarken ne test ettiğimi gerçekten tanımlamakta zorlanıyorum. Zamanlayıcı tarafından üretilen tüm zamanların kısıtlamalara uyması gerektiğini söyleyebilirim, ancak gerçek zamanları "rastgele" olmadan bu tür testleri geçen bir algoritma uygulayabilirim. Aslında olan tam olarak buydu; Saatlerin tam olarak tahmin edilebilir olmasa da izin verilen tarih / saat aralıklarının küçük bir alt kümesine düştüğü bir sorun buldum. Algoritma hala makul bir şekilde yapabileceğimi düşündüğüm tüm iddiaları geçti ve bu durumda başarısız olacak bir otomatik test tasarlayamadım, ancak "daha rastgele" sonuçlar verildiğinde geçtim. Sorunun, bazı testlerin kendilerine birkaç kez tekrarlanacak şekilde yeniden yapılandırılmasıyla çözüldüğünü göstermek zorunda kaldım ve görsel olarak oluşturulan zamanın tam izin verilen aralıkta olduğunu kontrol ettim.
Deterministik olmayan davranışlar beklemesi gereken testler tasarlamak için herhangi bir ipucu var mı?
Önerileriniz için herkese teşekkürler. Ana görüş , deterministik, tekrarlanabilir, iddialı sonuçlar elde etmek için deterministik bir teste ihtiyacım var gibi görünüyor . Mantıklı.
Sınırlama işlemi için aday algoritmaları içeren bir "sandbox" testi seti yarattım (herhangi bir uzunluğa sahip olabilecek bir bayt dizisinin bir dakika ile maksimum arasında uzun sürdüğü süreç). Daha sonra algoritmayı algoritma veren ve bilinen birkaç byte dizisini (sadece başlangıç için 1 ila 10,000,000 arası değerler) veren ve algoritmanın her birini 1009 ile 7919 arasında bir değere sınırlayan bir FOR döngüsü üzerinden çalıştırıyorum. algoritması giriş ve çıkış aralıkları arasında bazı serpitoptous GCF tarafından geçmez). Elde edilen sınırlanmış değerler sayılır ve bir histogram üretilir. "Geçiş" için, tüm girişler histogramın içine yansıtılmalıdır (hiçbirini "kaybetmememiz için sağlığımız) ve histogramdaki herhangi iki kova arasındaki fark 2'den büyük olamaz (gerçekten <= 1 olmalıdır) , ama bizi izlemeye devam edin). Varsa, kazanan algoritma doğrudan üretim koduna kesilebilir ve yapıştırılabilir ve regresyon için kalıcı bir test uygulanabilir.
İşte kod:
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;
}
... ve işte sonuçlar:
LSB reddi (sayıyı aralık içine düşene kadar kaydırmak) KORKUNÇtu, açıklaması çok kolay. herhangi bir sayıyı maksimumdan küçük olana kadar 2'ye böldüğünüzde, sonuçları en kısa sürede bırakabilirsiniz ve önemsiz olmayan herhangi bir aralık için sonuçları üst üçe doğru sapacak (histogramın ayrıntılı sonuçlarında görüldüğü gibi) ). Bu tam olarak bitmiş tarihlerden beri gördüğüm davranıştı; her zaman öğleden sonraydı, çok özel günlerde.
MSB reddi (aralıktakie kadar her seferinde bir numaradaki en önemli bitin çıkarılması) daha iyidir, ancak yine de, çünkü her bitle çok büyük sayıları kesersiniz, eşit olarak dağıtılmaz; Sayıları üst ve alt uçlarda elde etme ihtimaliniz yoktur, bu nedenle orta üçte birine karşı bir önyargı elde edersiniz. Bu, rastgele verileri bellish eğrisine "normalleştirmek" isteyen birisinin yararına olabilir, ancak iki veya daha fazla küçük rasgele sayıların (zar atmaya benzer) toplamı size daha doğal bir eğri verecektir. Amaçlarım için başarısız.
Bu testi geçen tek kişi, üçünün de en hızlı olduğu ortaya çıkan modulo bölünmesiyle kısıtlamaktı. Modulo, tanımı gereği, mevcut girdiler göz önüne alındığında mümkün olduğu kadar eşit bir dağılım üretecektir.