Thread-safe random number generator with unit testing support in C#

In a current project I have to deal with lots of multithreaded random number generation. The first challenge that I came upon was that standard .NET Random implementation is not thread-safe. When you call its methods from different threads, Random's inner state becomes mangled at some point and then all you get are zeroes. Uniform probability distribution? Yes. Random? Not so much :)

The obvious choice for me was to wrap Random instances in a ThreadLocal container and get and instance for each thread. Container had to be accessed with a static property on a static class. This solution worked flawlessly for some time, but then, as the project grew, it became obvious that some unit tests are in order.

As my friend said recently, there are two instruments of torture in programmers' hell: random numbers and parallelism. In my project I hit a jackpot. So I decided that in order to write simple and manageable tests, I need to exclude the randomness component from random number generation. And so I created a wrapper interface which I can easily mock in my tests.

First of all, I defined a non thread-safe interface and RNG implementation based on standard .NET implementation:

Note that in this implementation I use RNGCryptoServiceProvider to generate a random seed. This gives us a very good quality starting point without the performance impact of calling RNGCryptoServiceProvider all the time.

Now we must ensure actual thread safety. Let's create a simple wrapper to get us a different instance of IRandom for each thread:

Note the TestGenerator property. This is how we are going to inject a fake generator inside our unit tests.

Now let's create our fake RNG:

This class allows us to define a sequence of numbers that will appear through a series of calls to any method of IRandom. This gives us an ability to easily design predictable and simple unit tests.

Note that this class is disposable. We always need to use it in a using block so that our test random sequence doesn't affect other tests that maybe don't want to override the RNG.

So let's use our fake generator:

This test checks that a genetic algorithm selector can in fact select the right individual from a population (I know, right? :) ). As you can see, we don't explicitly use RNG in our test. Instead, it is used implicitly in our selector:

As you can see, this selector chooses a random item from the list using a uniform probability distribution. But when we override the RNG in our test, it always chooses an item number 2.

So this is quite simple but powerful technique which can completely remove such an undesirable factor as randomness in unit tests.