This is the fifth post in a series. The other post include:

If you want background, go read the earlier posts.

Based upon feedback, I am making the source code available at CWAB and Unity.

First off, I have moved this little side project to source control, so I can avoid the re-work I had to do before I could write this part of the series.

Note to self :

NEVER work without a safety net.  That means both unit tests and source control.

Ok.  Now that that is out of the way....

Let's go add Unity to CWAB, via EDD (formerly TDD).

How can we do this?  First, we need a test fixture.  So, I added CompositionContainerFixture to the CompositeWeb.Tests project as an empty test fixture.  And then I added the following very simple test as a starting point:

[TestMethod]
public void UnityCompositionContainerIsICompositionContainer()
{
    UnityCompositionContainer container = new UnityCompositionContainer();
    Assert.IsInstanceOfType(container, typeof(ICompositionContainer));
}

This will allow me to write enough code to create the UnityCompositionContainer class and ensure that it implements the right interface.

Making the test compile forces me to create a new UnityCompositionContainer class (I added it to the fixture as a nested class for the moment), and the test fails.  No problem.  I add the interface, implement it with methods that throw, and..... We are green again. 

Next step, I copied the unit test RootContainerParentIsNull over, changed its name to NewUnityContainerParentIsNull, updated it to create a new UnityCompositionContainer, and compiled and ran the test.  It passed, which is not idea;l, but I will leave it an move on, knowing it will be important later.  For the next test, I copied ChildContainerHasParent changed its name to NewUnityContainerParentIsNull, updated it to create a new UnityCompositionContainer, and compiled and ran the test... Red. Just like we expected.  To make this work, I could do a bit of throw away work, or I could add Unity.  I feel like I can skip the simplest thing possible, because I know where I want to go. 

However, first, I'm doing a check-in.  I'll comment out the failing test, and sync up.  Just a sec.

<click click>

Ok, now To add Unity.  I grabbed the binaries from MSDN, dropped them in my tree in the Lib folder and we are good to go.  I added references in the CWAB projecct to Unity.DLL and ObjectBuilder2.DLL.  This means we have two completely separate DI systems as part of out code, which is awful.  However, it won't last too long.

Now, we can make that test pass.  I added an IUnityContainer field to UnityCompositionContainer.  I then implemented a constructor to initialize it.  Then we needed to implement CreateChildContainer.  For this, the simplest thing was to create a new constructor for UnityCompositionContainer which takes a parent container as the only parameter.  After a little wire-up we end up with passing tests and this code:

private UnityCompositionContainer(UnityCompositionContainer parentContainer)
{
    Parent = parentContainer;
    wrappedContainer = parentContainer.wrappedContainer.CreateChildContainer();
}
public ICompositionContainer CreateChildContainer()
{
    UnityCompositionContainer child = new UnityCompositionContainer(this);
    return child;
}

Fairly simple.  I will continue moving unit tests over, adapting them, and making them pass.  If there are any interesting deviations from normal, or new tests added, I'll let you know.

<timewarp duration="a short while" />

That was actually fairly simple.  I copied the tests one at a time, replaced the container used with the UnityCompositionContainer, compiled, ran the tests, watched them fail due to NotImplementedExceptions, and fixed them.  Here's the test fixture:

[TestClass]
public class UnityCompositionContainerFixture
{
    [TestMethod]
    public void UnityCompositionContainerIsICompositionContainer()
    {
        UnityCompositionContainer container = new UnityCompositionContainer();
        Assert.IsInstanceOfType(container, typeof (ICompositionContainer));
    }

    [TestMethod]
    public void NewUnityContainerParentIsNull()
    {
        ICompositionContainer container = new UnityCompositionContainer();
        Assert.IsNull(container.Parent);
    }

    [TestMethod]
    public void ChildContainerHasParent()
    {
        ICompositionContainer parent = new UnityCompositionContainer();

        ICompositionContainer child = parent.CreateChildContainer();

        Assert.AreSame(parent, child.Parent);
    }

    [TestMethod]
    public void CanRegisterTypeMappingOnRootContainer()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();

        root.RegisterType<IFoo, Foo>();

        IFoo resolvedIFoo = root.Resolve<IFoo>();

        Assert.AreEqual(typeof (Foo), resolvedIFoo.GetType());
    }

    [TestMethod]
    public void CanRegisterTypeMappingViaTypeObjects()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();
        root.RegisterType(typeof (IFoo), typeof (Foo));

        IFoo resolvedIFoo = root.Resolve<IFoo>();

        Assert.AreEqual(typeof (Foo), resolvedIFoo.GetType());
    }

    [TestMethod]
    public void RequestingTypeMappingForUnmappedTypeReturnsRequestedType()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();

        Foo resolvedFoo = root.Resolve<Foo>();

        Assert.AreEqual(typeof (Foo), resolvedFoo.GetType());
    }

    [TestMethod]
    [ExpectedException(typeof (ArgumentException))]
    public void TypeMappingsMustBeTypeCompatible()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();
        root.RegisterType(typeof (IBar), typeof (Foo));
    }

    [TestMethod]
    public void CanRegisterMultipleTypeMappings()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();

        root.RegisterType<IFoo, Foo>();
        root.RegisterType<IBar, Bar>();
        IBar b = root.Resolve<IBar>();
        IFoo f = root.Resolve<IFoo>();

        Assert.AreEqual(typeof (Bar), b.GetType());
        Assert.AreEqual(typeof (Foo), f.GetType());
    }

    [TestMethod]
    public void RequestingTypeMappingOnChildReadsFromParent()
    {
        UnityCompositionContainer parent = new UnityCompositionContainer();
        ICompositionContainer child = (ICompositionContainer) parent.CreateChildContainer();

        parent.RegisterType<IFoo, Foo>();

        Assert.AreEqual(typeof (Foo), child.Resolve<IFoo>().GetType());
    }

    [TestMethod]
    public void ChildContainersCanOverrideParentTypeMapping()
    {
        UnityCompositionContainer parent = new UnityCompositionContainer();
        ICompositionContainer child = (UnityCompositionContainer) parent.CreateChildContainer();

        parent.RegisterType<IFoo, Foo>();
        child.RegisterType<IFoo, Foo2>();

        Assert.AreEqual(typeof (Foo), parent.Resolve<IFoo>().GetType());
        Assert.AreEqual(typeof (Foo2), child.Resolve<IFoo>().GetType());
    }

    [TestMethod]
    public void CanCreateChildContainer()
    {
        ICompositionContainer container = new UnityCompositionContainer();

        ICompositionContainer child = container.CreateChildContainer();
        Assert.IsNotNull(child);
        Assert.AreNotSame(container, child);
    }

    [TestMethod]
    public void CanRegisterInstance()
    {
        UnityCompositionContainer root = new UnityCompositionContainer();
        root.RegisterInstance(typeof (string), "foo", "bar");
        string returned = (string) root.Resolve(typeof (string), "foo");
        Assert.AreEqual("bar", returned);
    }


    [TestMethod]
    public void CanRegisterInstanceViaGenericWithoutName()
    {
        ICompositionContainer root = new UnityCompositionContainer();
        Foo f1 = new Foo();
        root.RegisterInstance<Foo>(f1);
        Foo returnedFoo = (Foo) root.Resolve(typeof (Foo));
        Assert.AreEqual(f1, returnedFoo);
    }

    [TestMethod]
    public void CanRegisterInstanceViaGenericWithName()
    {
        ICompositionContainer root = new UnityCompositionContainer();
        Foo f1 = new Foo();
        root.RegisterInstance<Foo>("asdf", f1);
        Foo returnedFoo = (Foo) root.Resolve(typeof (Foo), "asdf");
        Assert.AreEqual(f1, returnedFoo);
    }


    [TestMethod]
    public void CanResolveViaGenericWithoutName()
    {
        ICompositionContainer root = new UnityCompositionContainer();
        Foo f1 = new Foo();
        root.RegisterInstance<Foo>(f1);
        Foo returnedFoo = root.Resolve<Foo>();
        Assert.AreEqual(f1, returnedFoo);
    }

    [TestMethod]
    public void CanResolveInstanceViaGenericWithName()
    {
        ICompositionContainer root = new UnityCompositionContainer();
        Foo f1 = new Foo();
        root.RegisterInstance<Foo>("asdf", f1);
        Foo returnedFoo = root.Resolve<Foo>("asdf");
        Assert.AreEqual(f1, returnedFoo);
    }

    [TestMethod]
    public void UnityCompositionContainerImplementsIDisposable()
    {
        UnityCompositionContainer disposableContainer = new UnityCompositionContainer();
        Assert.IsNotNull(disposableContainer as IDisposable);
    }

    // This test is commented out intentionally. The purpose of this test is to show 
    // that if you use the generic version of the RegisterTypeMapping method, if the
    // types aren't compatible it'll fail at compile time. 
    // 
    // This is exactly what happens. However that also means this file won't compile.
    // The test is left in as comments, if you wish to verify this then remove the comments,
    // watch the compile file, and then comment it out again.

    //[TestMethod]
    //public void GenericTypeMappingRegistrationEnforcesCompileTimeCompatibility()
    //{
    //    UnityCompositionContainer root = new UnityCompositionContainer();
    //    root.RegisterType<IBar, Foo>();
    //}
}

Notice, there are no tests for the Services collection.  This is intentional.  We will see if we can avoid implementing the methods at all.

And here is the code to make those tests pass:

public class UnityCompositionContainer : ICompositionContainer, IDisposable
{
    private ICompositionContainer parent = null;
    private IUnityContainer wrappedContainer;

    public UnityCompositionContainer()
    {
        wrappedContainer = new UnityContainer();
    }

    private UnityCompositionContainer(UnityCompositionContainer parentContainer)
    {
        Parent = parentContainer;
        wrappedContainer = parentContainer.wrappedContainer.CreateChildContainer();
    }

    public ICompositionContainer Parent
    {
        get { return parent; }
        set { parent = value; }
    }

    public IServiceCollection Services
    {
        get { throw new NotImplementedException(); // return services; 
        }
    }

    public void RegisterInstance(Type t, object instance)
    {
        wrappedContainer.RegisterInstance(t, instance);
    }

    public void RegisterInstance(Type t, string name, object instance)
    {
        wrappedContainer.RegisterInstance(t, name, instance);
    }

    public void RegisterInstance<TInterface>(TInterface instance)
    {
        wrappedContainer.RegisterInstance<TInterface>(instance);
    }

    public void RegisterInstance<TInterface>(string name, TInterface instance)
    {
        wrappedContainer.RegisterInstance<TInterface>(name, instance);
    }

    public void RegisterType<TRequested, TReturned>() where TReturned : TRequested
    {
        wrappedContainer.RegisterType<TRequested, TReturned>();
    }

    public void RegisterType(Type requested, Type returned)
    {
        wrappedContainer.RegisterType(requested, returned);
    }

    public object Resolve(Type typeOfItem)
    {
        return wrappedContainer.Resolve(typeOfItem);
    }

    public object Resolve(Type typeOfItem, string name)
    {
        return wrappedContainer.Resolve(typeOfItem, name);
    }

    public T Resolve<T>()
    {
        return wrappedContainer.Resolve<T>();
    }

    public T Resolve<T>(string name)
    {
        return wrappedContainer.Resolve<T>(name);
    }

    public object BuildItem(IBuilder<WCSFBuilderStage> builder, object item)
    {
        throw new NotImplementedException();
    }

    public ICompositionContainer CreateChildContainer()
    {
        UnityCompositionContainer child = new UnityCompositionContainer(this);
        return child;
    }

    public void Dispose()
    {
        wrappedContainer.Dispose();
        wrappedContainer = null;
    }
}

We are now in a green state, time to check in.

Abusing Source Control

Agile teams usually check in very often.  Some teams check in every time they hit green.  I think that is a bit of overkill, unless you have a source control system with NO overhead.  I check in when I complete a feature or a story, and usually at a few good, green stopping points in the process, like I have been doing.  This was a good stopping point, and my next task is a bit risky, so this is a perfect time to check in.

This is a long enough post, even though I didn't do all that much, that I will call it quits for today.  Next post, actually use the UnityCompositionContainer, and then nuke the old CompositionContainer.