Summary
I’ve done quite a few posts on unit testing in the past. I keep a list of subjects that I would like to blog about so I have a ready list to choose from. My list of unit testing subjects is getting large and it’s time to clear the spindle. So in this post I’m going to do some deep diving on Dependency Injection and introduce Inversion Of Control using Autofac.
The Die Roller
I created a simple program a while back that does a die roll (you can find it by clicking here). I had hoped to write some follow up posts about other methods that can be used to get around the problem of object dependency, but other blog subjects grabbed my interest and took up my time. So now I’m going to go back and discuss other techniques that I know in order to break or eliminate dependencies in objects.
First, I’m going to show a technique that uses a singleton. The idea behind this design pattern is to provide a default object that will self-instantiate when it is called from the main program, but provide an entry point (a setter) that will allow the object to be overridden by a fake object in a unit test before the object under test is called. I’ve blogged about this technique in this post where I described a technique to design a caching system.
The base object looks like this:
public abstract class DieRollerBase { public static DieRollerBase _Instance; public static DieRollerBase Instance { get { if (_Instance == null) { _Instance = new DieRoller(); } return _Instance; } set { _Instance = value; } } public static int DieRoll() { return Instance.ReturnDieRoll(); } public abstract int ReturnDieRoll(); }
The die roller object, which is run inside the main program looks like this:
public class DieRoller : DieRollerBase { private Random RandomNumberGenerator = new Random(DateTime.Now.Millisecond); public override int ReturnDieRoll() { return RandomNumberGenerator.Next() % 6; } }
As you can see, the base class instantiates a new DieRoller() object, instead of a DieRollerBase object. What happens is the main program will call the die roller using the following syntax:
int result = DieRoller.DieRoll();
The call to the method DieRoll() is static, but it calls the instance method called ReturnDieRoll() which is implemented inside the sub-class, not the base class. The reason for doing this is that we can override the DieRoll() class with a fake class like this:
public class FakeDieRoller : DieRollerBase { private static int _NextDieRoll = 0; private static List _SetDieRoll = new List(); public static int SetDieRoll { get { int nextDieRoll = _SetDieRoll[_NextDieRoll]; _NextDieRoll++; if (_NextDieRoll >= _SetDieRoll.Count) { _NextDieRoll = 0; } return nextDieRoll; } set { _SetDieRoll.Add(value); } } public static void ClearDieRoll() { _SetDieRoll.Clear(); _NextDieRoll = 0; } public override int ReturnDieRoll() { return SetDieRoll; } }
Using the setter of the base class for the instance, we can do this in our unit test:
DieRoller.Instance = new FakeDieRoller();
Any method calling the die roller will end up executing the fake class instead of the default class. The reason for doing this is so we can load the dice by stuffing “known” die roll numbers into the dice before calling our object under test. Then we can get predictable results from objects that use the random die roll object.
Analysis
In my earlier blog post I created a die roller class that looked like this:
public static class DieRoller { private static Random RandomNumberGenerator = new Random(DateTime.Now.Millisecond); public static int DieRoll() { if (UnitTestHelpers.IsInUnitTest) { return UnitTestHelpers.SetDieRoll; } else { return RandomNumberGenerator.Next() % 6; } } }
The injection took place using the UnitTestHelpers object. This tests if the startup dll was a Microsoft test object and then executed a built-in fake die. This is not a clean technique for unit testing since there is some test code compiled into the distributed dlls. Mainly, the UnitTestHelpers.SetDieRoll method.
The singleton method is much cleaner, because the fake object can be created inside the unit test project and not distributed with the production dlls. Therefore the final code will not contain the fake die object or any of the test code. The problem with singletons is that they are complicated to design.
There is a better technique. It’s called Inversion Of Control or IOC. The idea behind inversion of control is that objects are created independent of each other, then they are “wired” together at program initialization time. Unit tests can link fake objects before the tests are executed, which automatically bypasses the dependent objects that are not under test. This approach is cleaner and I’m going to show the die roller using the Autofac IOC container.
Setting up the Solution
Autofac has an object called the container. The container is like a dictionary storage place where all the classes and interfaces are stored when the program is initialized. Then the resolve command uses the container information to match which class is setup for each interface. Inside your class, you’ll call the resolve command and pass the interface, without any reference to the object class itself. This allows Autofac to set which class will be used for the interface when is needed. By doing this, we can setup a different class (like a fake class) inside a unit test and the object under test will call the resolve command with the same interface but the fake object will already be instantiated by Autofac to be used.
So here are the projects I used in my little demo program:
Container DieRollerAutoFac DieRollerLibrary GameLibrary DieRollerTests
The program itself will start from the DieRollerAutoFac project. This is just a console application that initializes the IOC container and runs the game. The IOC container is stored in a static class called IOCContainer and it’s inside the “Container” library. The reason I structured it this way is so I can use the container for the program and I can use the container for the unit tests. I also needed the container for the game class when it performs the resolve operation. So the container must be in a different project to keep it from being dependent on the game class or the main program.
Next, I created the die roller class and interface inside it’s own project. This could be contained inside the GameLibrary project, but I’m going to pretend that we want to isolate modules (i.e. dlls).
Next we need to wire everything up. If you download the sample code and look at the main program, you’ll see this piece of code:
var builder = new ContainerBuilder(); builder.RegisterType<DieRoller>().As<IDieRoller>(); IOCContainer.Container = builder.Build();
This is the code that builds the container. Once the container is built, it can be used by any object in the program.
Inside the game class the container is used to resolve the die object:
public class Game { public int Play() { using (var scope = IOCContainer.Container.BeginLifetimeScope()) { var die = scope.Resolve(); return die.DieRoll(); } } }
To substitute a fake die class in your unit tests, you can do this:
var builder = new ContainerBuilder(); builder.RegisterType<FakeDieRoller>().As<IDieRoller>(); IOCContainer.Container = builder.Build();
Checklist of IOC container rules:
1. Use the NuGet manager to install Autofac.
2. Make sure you create an interface for each object you intend to use in your IOC container.
3. Setup a container creator in your unit tests with fake or mock objects.
Where to get the code
As always I have posted all the code from this blog post. You can download the source by clicking here