What you actually want to test here, I assume, is that given a specific set of results from the randomiser, the rest of your method performs correctly.
If that's what you're looking for then mock out the randomiser, to make it deterministic within the realms of the test.
I generally have mock objects for all kinds of non-deterministic or unpredictable (at the time of writing the test) data, including GUID generators and DateTime.Now.
Edit, from comments: You have to mock the PRNG (that term escaped me last night) at the lowest level possible - ie. when it generates the array of bytes, not after you turn those into Int64s. Or even at both levels, so you can test your conversion to an array of Int64 works as intended and then test separately that your conversion to an array of DateTimes works as intended. As Jonathon said, you could just do that by giving it a set seed, or you can give it the array of bytes to return.
I prefer the latter because it won't break if the framework implementation of a PRNG changes. However, one advantage to giving it the seed is that if you find a case in production that didn't work as intended, you only need to have logged one number to be able to replicate it, as opposed to the whole array.
All this said, you must remember that it's called a Pseudo Random Number Generator for a reason. There may be some bias even at that level.
I don't think unit tests are the right tool for testing randomness. A unit test should call a method and test the returned value (or object state) against an expected value. The problem with testing randomness is that there isn't an expected value for most of the things you'd like to test. You can test with a given seed, but that only tests repeatability. It doesn't give you any way to measure how random the distribution is, or if it's even random at all.
Fortunately, there are a lot of statistical tests you can run, such as the Diehard Battery of Tests of Randomness. See also:
How to unit test a pseudo random number generator?
- Steve Jessop recommends that you find a tested implementation of the same RNG algorithm that you're using and compare its output with selected seeds against your own implementation.
- Greg Hewgill recommends the ENT suite of statistical tests.
- John D. Cook refers readers to his CodeProject article Simple Random Number Generation, which includes an implementation of the Kolmogorov-Smirnov test mentioned in Donald Knuth's volume 2, Seminumerical Algorithms.
- Several people recommend testing that the distribution of the numbers generated is uniform, the Chi-squared test, and testing that the mean and standard deviation are within the expected range. (Note that testing the distribution alone is not enough. [1,2,3,4,5,6,7,8] is a uniform distribution, but it's certainly not random.)
Unit Testing with functions that return random results
- Brian Genisio points out that mocking your RNG is one option for making your tests repeatable, and provides C# sample code.
- Again, several more people point to using fixed seed values for repeatability and simple tests for uniform distribution, Chi-squared, etc.
Unit Testing Randomness is a wiki article that talks about many of the challenges already touched on when trying to test that which is, by its nature, not repeatable. One interesting bit that I gleaned from it was the following:
I've seen winzip used as a tool to measure the randomness of a file of values before (obviously, the smaller it can compress the file the less random it is).
Best Answer
Languages tend to have their own implementations of pseudo-random number generators, so you cannot know how a pseudo-random number generator works in any programming language. Also, there is not much value that we could add here besides pointing you to the relevant article in Wikipedia.
The simplest approach, however, which produces fairly good results and is very fast, is to keep track of the last integer issued, and each time a new pseudo-random integer is requested, increment it by one and then multiply it by a huge prime constant before returning it. This multiplication usually results in substantial overflow, which is ignored. The effect is that the resulting bit pattern is for all practical purposes random. The increment by one is necessary in order to ensure that if the number ever reaches zero, subsequent multiplications will not keep yielding zero.
I do not know how you got the impression that pseudo-random numbers have short periods, because this is simply not true. Pseudo-random number generators tend to have huge periods.
A pseudo-random number generator will use its own seed only if you do not specify your own seed. If you specify your own seed, then the pseudo-random number generator will use your seed.
Do try this at home:
Write a program which draws a starry sky and then clears it, using only
GetPixel()
andSetPixel()
.So, what you have to do is write a loop which generates, say, 1000 pseudo-random pairs of coordinates, and inverts each pixel at those coordinates. If the pixel was black, it makes it white. If it was white, it makes it black. If the screen was initially all black, about 1000 white stars should appear.
Now, to clear the stars, you could clear the entire screen, but there is a cooler way to do it.
Initialize your pseudo-random number generator with the exact same seed, and then re-apply the above loop. The exact same sequence of pseudo-random numbers will be generated, therefore the exact same set of coordinates will be visited, so each one of those pixels will be re-inverted, resulting in a black sky again.