Dự án hiện tại của tôi, ngắn gọn, liên quan đến việc tạo ra "các sự kiện ngẫu nhiên ngẫu nhiên". Về cơ bản tôi đang tạo ra một lịch trình kiểm tra. Một số trong số họ được dựa trên các ràng buộc lịch trình nghiêm ngặt; bạn thực hiện kiểm tra mỗi tuần một lần vào thứ Sáu lúc 10:00 sáng. Kiểm tra khác là "ngẫu nhiên"; có các yêu cầu cấu hình cơ bản như "kiểm tra phải diễn ra 3 lần mỗi tuần", "kiểm tra phải diễn ra trong khoảng thời gian từ 9 giờ sáng đến 9 giờ tối" và "không nên có hai lần kiểm tra trong cùng khoảng thời gian 8 giờ", nhưng trong bất kỳ ràng buộc nào được định cấu hình cho một bộ kiểm tra cụ thể, ngày và thời gian kết quả sẽ không thể dự đoán được.
Các thử nghiệm đơn vị và TDD, IMO, có giá trị lớn trong hệ thống này vì chúng có thể được sử dụng để tăng dần nó trong khi toàn bộ các yêu cầu của nó vẫn chưa hoàn thành và đảm bảo rằng tôi không "quá kỹ thuật" để làm những việc tôi không làm Hiện tại tôi biết tôi cần. Lịch trình nghiêm ngặt là một miếng bánh cho TDD. Tuy nhiên, tôi cảm thấy rất khó để xác định những gì tôi đang kiểm tra khi tôi viết các bài kiểm tra cho phần ngẫu nhiên của hệ thống. Tôi có thể khẳng định rằng tất cả các thời gian được tạo bởi bộ lập lịch phải nằm trong các ràng buộc, nhưng tôi có thể thực hiện một thuật toán vượt qua tất cả các thử nghiệm như vậy mà không có thời gian thực tế là rất "ngẫu nhiên". Trong thực tế đó chính xác là những gì đã xảy ra; Tôi đã tìm thấy một vấn đề trong đó thời gian, mặc dù không thể dự đoán chính xác, rơi vào một tập hợp nhỏ của phạm vi ngày / thời gian cho phép. Thuật toán vẫn vượt qua tất cả các xác nhận mà tôi cảm thấy có thể đưa ra một cách hợp lý và tôi không thể thiết kế một thử nghiệm tự động sẽ thất bại trong tình huống đó, nhưng vượt qua khi cho kết quả "ngẫu nhiên hơn". Tôi đã phải chứng minh vấn đề đã được giải quyết bằng cách cơ cấu lại một số thử nghiệm hiện có để lặp lại một số lần và kiểm tra trực quan rằng thời gian được tạo ra có nằm trong phạm vi cho phép đầy đủ hay không.
Có ai có bất cứ lời khuyên để thiết kế các bài kiểm tra nên mong đợi hành vi không xác định?
Cảm ơn mọi lời gợi ý. Ý kiến chính dường như là tôi cần một bài kiểm tra xác định để có được kết quả xác định, có thể lặp lại, có thể khẳng định . Có ý nghĩa.
Tôi đã tạo ra một tập hợp các bài kiểm tra "hộp cát" có chứa các thuật toán ứng cử viên cho quy trình ràng buộc (quá trình mà một mảng byte có thể dài trở thành một khoảng dài giữa một phút và tối đa). Sau đó, tôi chạy mã đó thông qua một vòng lặp FOR cung cấp cho thuật toán một số mảng byte đã biết (các giá trị từ 1 đến 10.000.000 chỉ để bắt đầu) và có thuật toán ràng buộc từng giá trị trong khoảng từ 1009 đến 7919 (Tôi đang sử dụng các số nguyên tố để đảm bảo thuật toán sẽ không vượt qua một số GCF ngẫu nhiên giữa các phạm vi đầu vào và đầu ra). Các giá trị ràng buộc kết quả được tính và biểu đồ được tạo ra. Để "vượt qua", tất cả các yếu tố đầu vào phải được phản ánh trong biểu đồ (sự tỉnh táo để đảm bảo chúng tôi không "mất" bất kỳ) và chênh lệch giữa hai nhóm trong biểu đồ không thể lớn hơn 2 (thực sự phải là <= 1 , nhưng hãy theo dõi). Thuật toán chiến thắng, nếu có, có thể được cắt và dán trực tiếp vào mã sản xuất và thử nghiệm vĩnh viễn được đưa ra để hồi quy.
Đây là mã:
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;
}
... Và đây là kết quả:
Từ chối LSB (thay đổi số bit cho đến khi nó nằm trong phạm vi) là TERRIBLE, vì một lý do rất dễ giải thích; khi bạn chia bất kỳ số nào cho 2 cho đến khi nó nhỏ hơn một mức tối đa, bạn sẽ thoát ngay khi có và với bất kỳ phạm vi không tầm thường nào, điều đó sẽ làm sai lệch kết quả về phía trên thứ ba (như đã thấy trong kết quả chi tiết của biểu đồ ). Đây chính xác là hành vi tôi thấy từ những ngày kết thúc; tất cả thời gian là vào buổi chiều, vào những ngày rất cụ thể.
Từ chối MSB (loại bỏ bit đáng kể nhất ra khỏi số một tại một thời điểm cho đến khi nó nằm trong phạm vi) sẽ tốt hơn, nhưng một lần nữa, vì bạn đang cắt các số rất lớn với mỗi bit, nó không được phân bổ đều; bạn không có khả năng nhận được số ở đầu trên và dưới, vì vậy bạn có xu hướng về thứ ba giữa. Điều đó có thể có lợi cho ai đó đang tìm cách "bình thường hóa" dữ liệu ngẫu nhiên thành một đường cong hình chuông, nhưng tổng hai hoặc nhiều số ngẫu nhiên nhỏ hơn (tương tự như ném xúc xắc) sẽ cho bạn đường cong tự nhiên hơn. Đối với mục đích của tôi, nó thất bại.
Người duy nhất vượt qua bài kiểm tra này là hạn chế phân chia modulo, điều này cũng hóa ra là nhanh nhất trong ba. Modulo, theo định nghĩa của nó, sẽ tạo ra một bản phân phối càng nhiều càng tốt với các đầu vào khả dụng.