System.Random isolation

Writing a unit test is all about isolating the code under test. The best way to do this is by using interfaces. An interface only described the methods and properties, but lets you change the implementation as long as you use the same signature.

The default framework objects are no exception, except there (almost) never is an interface defined. Isolation something like System.Random in your unit test can be hard. Here’s my way of doing it.

IRandom

IRandom

First I’ve defined the interface for the Random object. In there are the public methods my program needs. The System.Random object does not implement this IRandom interface. I have to do this myself. More on that later.

Using this interface I can replace the Random object with a stub. More on that later too.

IRandomFactory

IRandomFactory

Another interface defines the constructors of the Random object. I’ve defined a Create method for every constructor of Random my program uses. This interface is the key to my isolation method.

Random

Random

In my own namespace (Company.SystemIsolation) I create the Random class that implements the IRandom interface. This is the object my program is going to use.

In the Reset method the Factory property is set to the SystemRandomFactory defined below, for now all you need to know it results in using the System.Random.

The Factory property is static and is used to create an instance saved in Implementation in the constructors of the Random object.

public Random(int seed) {
   Implementation = Factory.Create(seed);
}

Every method in the IRandom interface is implemented with a redirect to the IRandom method of the Implementation property.

public int Next() {
   return Implementation.Next();
}

Still with me? The hard part is over, just soak up this last two default implementations and you are in isolation zen.

SystemRandomWrapper

SystemRandomWrapper

As stated above the System.Random object does not implement our IRandom interface. This can be solved using the wrapper design pattern.

The System.Random object is passed into the constructor to instantiate the Implementation member.

Now every call to the interface is redirected to the Implementation. Sounds familiar? Right, just like the Random object does, only the Implementation is of a different type.

SystemRandomFactory

SystemRandomFactory

To create a SystemRandomWrapper there must be a Factory that provides it. This implements the IRandomFactory interface by creating a System.Random object and passing it to the SystemRandomWrapper constructor. The result is a System.Random object that implements the IRandom interface.

Ioslate (optional)

For code cleanliness I’ve created a static class for replacing the Factory in the Random class. The method is called Isolate and takes an IRandomFactory parameter.

The class also contains a Reset method that calls the Reset method of the Random class. This can be expanded with the Reset method of other isolated objects.

Sample

To use the isolation in existing code do a find-and-replace of System.Random with SystemIsolation.Random. Maybe you need to fix some references, but the idea is the default behavior remains unchanged. Now you gained a way to isolate your tests from the Random object.

// ARRANGE
// our fake random object to use in the unittest
var fakeRandom = MockRepository.GenerateStub<IRandom>();
fakeRandom.Expect(x => x.Next()).Return(9);
// our fake random factory that returns our fake random object
var fakeFactory = MockRepository.GenerateStub<IRandomFactory>();
fakeFactory.Expect(x => x.Create()).Return(fakeRandom);
// use our fake setup in the execution to create fake random objects
SystemIoslation.Random.Factory = fakeFactory;
// create your testobject and ACT and ASSERT

Take aways

To create another isolation create the two interfaces (one with the methods/properties of the object and one for the factory), then create the entry class that uses the interfaces and last create the default implementation for the two interfaces.

This all seems like a lot of work and it is. Using a code generator can do the big chunks but you will always need some manual labor. But when you are done, you can use it again and again in all your projects. I’m expanding my isolation library on every project and happily reuse my hard work from older projects.

Download the Demo project.

About erictummers

Working in a DevOps team is the best thing that happened to me. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Test and tagged , , , , , , . Bookmark the permalink.

6 Responses to System.Random isolation

  1. Pingback: System.DateTime isolation | Erictummers's Blog

  2. why didn’t you show the Interfaces code? good post though

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.