I have been doing some unit testing and mocking in a new application I am working on and I have come to appreciate the need for an IOC Container. I know there are several well established IOC Container frameworks out there (http://csharp-source.net/open-source/containers) but it seemed like such a simple idea I decided to make my own. I created a very very simple Container using lambda expressions and Func<> delegates.
public static class Container { private static Dictionary<Type, Delegate> _registrations = newDictionary<Type, Delegate>(); public static void Initialize() { _registrations.Clear(); } public static void Register<TResult>(Func<TResult> create) { Register(typeof(TResult), create); } public static void Register<T, TResult>(Func<T, TResult> create) { Register(typeof(TResult), create); } public static void Register<T1, T2, TResult>(Func<T1, T2, TResult> create) { Register(typeof(TResult), create); } public static void Register(Type type, Delegate create) { if (_registrations.ContainsKey(type)) throw new InvalidOperationException("Same key registered twice"); _registrations.Add(type, create); } public static T Create<T>(paramsobject[] args) { Type key = typeof(T); if (!_registrations.ContainsKey(key)) throw new InvalidOperationException("Key not registered in container"); return (T)_registrations[typeof(T)].DynamicInvoke(args); } }
So there are really three methods, Initialize, Create and Register. The last register is the most important the rest are simple helpers.
So imagine in your application you had the following dependency situation:
public class Foo { IBar _bar; public Foo() { _bar = new Bar(this); } } public class Bar { public Bar(IFoo foo) { } }
public interface IFoo { } public interface IBar { } public class Foo : IFoo { IBar _bar; public Foo() { _bar = Container.Create<IBar>(this); } } public class Bar : IBar { public Bar(IFoo foo) { } }
Instead of directly instantiating Bar we are now using the container to create an IBar for us. So now all we have to do is register a type with the container at either application initialization or in the test Setup method. For example:
Container.Register<IFoo>(() => { returnnewFoo(); }); Container.Register<IFoo, IBar>((IFoo f) => { returnnewBar(f); }); IFoo foo = Container.Create<IFoo>();
Calling Create on the Container for IFoo will result in calling the IFoo registered lambda expression which will result in another call to the IBar registration. This breaks the dependencies so we can effectively test the Foo class now. Here is an example of how you might mock the Bar class using Rhino mocks:
IBar mockBar = MockRepository.GenerateMock<IBar>(); Container.Register<IFoo, IBar>((IFoo f) => { return mockBar; }); IFoo foo = newFoo();
We are generating a mock IBar then registering it in the container. Now when we call the constructor on Foo it will use the container to create an IBar which will pass it the mock object we created. From here we can setup expectations and verify all values on foo as normal without having to worry about side effects.