Testing composition root using Castle Windsor

Why?

Composition is not the first thing people strive to white-box test. As almost everything depends on composition root working, mistakes made in component wiring manifest themselves quickly. Programmers tend to run their application before they push their changes, so a constructor dependency missing from registration won’t even make it to testing environments.

Nevertheless.

  • “The application will compose as every constructor dependency is defined in composition root” is not the only property we might test for. Lifestyle mismatches like “Singleton depending on a PerWebRequest service” is something that won’t be spotted quickly and might show only under load.

  • If you are using installers to partition your registration logic, you might consider enforcing closure of individual installers under depends-on relation. This is especially important if you reuse installers across different composition roots. It’s not given that developer making changes for one application will have in mind that the changes she made to a shared installer will affect another roots.

  • If you are using live testing tooling like NCrunch, it might be actually much more comfortable to be told about broken composition as soon as you edit your constructor, without trying to run the app. Properly written composition root does not depend on database or network, and so resulting test will be very fast and cheap.

Test 1: Does it compose?

This is the standard snippet used to check Castle Windsor wiring:

[TestMethod]
void RootComposes()
{
    WindsorContainer container = CompositionRoot.Get();

    var host = (IDiagnosticsHost)container.Kernel.GetSubSystem(SubSystemConstants.DiagnosticsKey);
    var misconfigurations = host.GetDiagnostic<IPotentiallyMisconfiguredComponentsDiagnostic>().Inspect();

    if (misconfigurations.Any())
    {
        var diagnosticOutput = new StringBuilder();

        foreach (IExposeDependencyInfo problem in misconfigurations)
        {
            problem.ObtainDependencyDetails(new DependencyInspector(diagnosticOutput));
        }

        Assert.Fail(diagnosticOutput.ToString());
    }
}

Such check will notice situations like

class ImapMailReceiver : IMailReceiver
{
    public ImapMailReceiver(IImapConfiguration imapConfiguration) { }
}

static class CompositionRoot
{
    public static WindsorContainer Get()
    {
        var container = new WindsorContainer();

        container.Register(
            Component.For<IMailReceiver>().ImplementedBy<ImapMailReceiver>().LifestyleSingleton()
        );

        return container;
    }
}

i.e., situations when registered implementation cannot get its dependencies fulfilled (as IImapConfiguration is not even mentioned in CompositionRoot.Get). The error is nicely descriptive:

'ImapMailReceiver' is waiting for the following dependencies:
- Service 'IImapConfiguration' which was not registered.

Implicit dependencies in wiring logic

This works as long as you don’t depend on your environment too much in CompositionRoot.Get(). An implicit dependency on appsettings such as this will probably kill your test:

static class CompositionRoot
{
    public static WindsorContainer Get()
    {
        var container = new WindsorContainer();

        container.Register(
            Component
                .For<IMailReceiver>()
                .ImplementedBy<ImapMailReceiver>()
                .DependsOn(Dependency.OnValue("imapAddress", ConfigurationManager.AppSettings["ImapAddress"]))
                .LifestyleSingleton()
        );

        return container;
    }
}

Let’s be thorough here. The standard way of doing dependency injection have three distinct phases:

  • There is registration time, when we run the code wiring components together. It happens once, at the start of the application. The logic guiding this process is something we strive to test in this article.
  • There is resolve time, when constructors are being ran with dependencies injected by Windsor. This happens per-request in ASP.NET context, for example.
  • … and then our actual non-wiring, non-constructor code starts to run, enjoying the luxury of everything needed nicely prepared.

It’s widely recognized that the constructor logic (the resolve time logic) should not be heavy, both in terms of computing capacity required, and in terms of dependencies on the rest of the system.

I think we should hold such standards even more stringently at registration time: making wiring implicitly dependent on a working database connection or domain-level configuration presence is worse than letting constructor block on network communication. There are practical reasons – we don’t want to loose all functionality with just part of the necessary setup missing – but there are also architectural concerns: it’s just not composition root’s job to care about settings that should be owned by component internals. It’s a detail from composition’s point of view whether there is a prefix or not in some network configuration key.

This is important for us because we want to run CompositionRoot.Get() in a unit test, as we saw in the code example. Such unit test won’t have our usual environment set up and we can’t mock anything without

  • envisioning wiring process with several stages (having CompositionRoot.Get(IAppSettingsProvider asp, ...)), or,
  • without editing global state in the test (ConfigurationManager.AppSettings["ImapAddress"] = "...";) and by doing that, throwing our test parallelization options out of the window.

It’s not at all difficult to move all the logic from wiring and constructors into the actual run time:

  • For every configuration check, you either wrap the access behind an configuration interface (interface IImapConfiguration { string ServerAccess { get; set; } }), or even microtype the value needed (class ImapServerAddress { ... }). The latter is arguably more practical in languages that expect us to actually use type system in our daily work (like F#).
  • For every database/network access, you just move operations into service methods or add a factory service that would wrap it.

What I’m trying to argue here is that there is a larger cause for refactoring configuration, database and network dependencies outside of wiring code. In this sense, you should feel good about having a pretext for finally doing it.

Test 2: Are lifetimes matching?

Curious person might ask whether the line host.GetDiagnostic<IPotentiallyMisconfiguredComponentsDiagnostic>().Inspect() can be modified to get access to diagnostics other than the one looking for PotentiallyMisconfiguredComponents.

It can! IPotentialLifestyleMismatchesDiagnostic looks through the dependency tree for components with long lifestyle depending on components with short ones: cases like singleton components with dependencies on transient components, or singletons depending on PerWebRequests.

Such dependencies result in an unintuitive behavior: singleton will hold its instance of “transient” dependency for long time, breaking potential assumptions on transient service longevity.

[TestMethod]
void RootDoesNotMismatchLifestyles()
{
    var container = CompositionRoot.Get();

    var host = (IDiagnosticsHost)container.Kernel.GetSubSystem(SubSystemConstants.DiagnosticsKey);
    var misconfigurations = host.GetDiagnostic<IPotentialLifestyleMismatchesDiagnostic>().Inspect();

    if (misconfigurations.Any())
    {
        var diagnosticOutput = new StringBuilder();

        diagnosticOutput.AppendLine("Following dependency chains have inconsistent accessibility:" + Environment.NewLine);

        foreach (var misconfiguration in misconfigurations)
        {
            diagnosticOutput.AppendLine(
                string.Join(
                    " -> ",
                    misconfiguration
                        .Where(m => m.ComponentModel.Services.Any())
                        .Select(m => $"{m.ComponentModel.Services.First().Name} ({m.ComponentModel.LifestyleType})")));
        }

        Assert.Fail(diagnosticOutput.ToString());
    }
}

You might run into a case when this kind of dependency is desirable. Adding list of exceptions into the test is straightforward then.

Test 3: Are there any useless registrations?

One of the disadvantages of dependency injection is the reduced ability to use static reasoning over the codebase. We can see interfaces mentioned in constructor arguments, we can see methods called over these interfaces, but we can’t “go to definition” just as easily as we were used to in the non-polymorphic world of yesterday.

Specially, when we remove the last dependency to some service, we won’t see “0 references” in Visual Studio, because there is still this basic mention – registration in composition root.

The question arise: can we test that there are registrations that won’t be used? The answer is sure, as long as

  • we are willing to code the logic for ourselves (there is not any diagnostic already prepared), and, crucially,
  • we know what’s going to be resolved. Remember, Resolve<T> is a runtime phenomenon and when trying to reason about this, we have to be sure that any service we register is not needed in this most trivial sense, not as a dependency, but as a Resolved root our app starts from. In ASP.NET setting, these root services are usually controllers: almost everything depends on them, but, usually, they are not needed for any other service in the formal, constructor-injection-y way.

After this is settled, a simple graph traversal starting from our root (to-be-resolved) services will do:

[TestMethod]
public void ServicesAreUsed()
{
    var coreServices = new[] {
        "Namespace.OrderController",
        "Namespace.CustomerController",
        "Namespace.ReportController"
    };

    var container = CompositionRoot.Get();

    var services = container.Kernel.GraphNodes.Where(n => n is ComponentModel).Cast<ComponentModel>();
    var usefulServices = new HashSet<ComponentModel>();
    var servicesToProcess = new Stack<ComponentModel>(
        services.Where(service =>
            coreServices.Any(coreService =>
                service.ComponentName.Name == coreService)));

    while (servicesToProcess.TryPop(out var service))
    {
        usefulServices.Add(service);
        
        foreach (var subservice in service.Dependents.Where(n => n is ComponentModel).Cast<ComponentModel>())
        {
            servicesToProcess.Push(subservice);
        }
    }

    var unusefulServices = new HashSet<ComponentModel>(services);
    unusefulServices.ExceptWith(usefulServices);

    Assert.AreEqual(
        0, unusefulServices.Count,
        "Following services are registered and are not needed: " + string.Join(", ", unusefulServices));
}

Test 4: Are we using composition root as a service locator?

We can’t stop people from doing things like this:

public void ProcessPayment(...)
{
    // ...

    var ccProcessor = CompositionRoot.Get().Resolve<ICreditCardProcessor>();
    ccProcessor.Process(...);

    // ...
}

What can be spotted though are slightly more civilized transgressions that do not shamefully try to call static (gasp!) methods to get what they need, but that are still similar in the harmful core approach of service locator: making your service dependent on an unspecified whole world of IWindsorContainer instead of descriptive and statically checkable interfaces of actual subservices.

public class PaymentProcessingService
{
    private readonly IWindsorContainer _container;

    public PaymentProcessingService(IWindsorContainer container)
    {
        _container = container;
    }

    public void ProcessPayment(...)
    {
        // ...

        var ccProcessor = _container.Resolve<ICreditCardProcessor>();
        ccProcessor.Process(...);

        // ...
    }
}

Now that the container knows it’s being abused, IUsingContainerAsServiceLocatorDiagnostic diagnostic can be applied:

[TestMethod]
public void ServiceLocatorNotPresentTest()
{
    var container = CompositionRoot.Get();

    var host = (IDiagnosticsHost)container.Kernel.GetSubSystem(SubSystemConstants.DiagnosticsKey);
    var locators = host.GetDiagnostic<IUsingContainerAsServiceLocatorDiagnostic>().Inspect();

    if (locators.Any())
    {
        var diagnosticOutput = new StringBuilder();

        diagnosticOutput.AppendLine("Following components looks suspiciously like a service locator:" + Environment.NewLine);
        foreach (var locator in locators)
        {
            diagnosticOutput.AppendLine(locator.ToString());
        }

        Assert.Fail(diagnosticOutput.ToString());
    }
}

It’s useful to notice that the implementation of the diagnostic just checks against a list of too-general services well-behaved service should not depend upon. It’s a simple mechanism.

Test 5: Are root services resolvable?

Before the end, let’s return to our earlier considerations about stages of dependency injection. Because we succeeded at testing wiring, we might proceed towards checking resolvability (i.e., testing the second stage) of the root services of our application. We are going to use the very same “service roots” we talked about in the third part.

[DataTestMethod]
[DataRow(typeof(OrderController))]
[DataRow(typeof(CustomerConstroller))]
[DataRow(typeof(ReportController))]
public void ControllersCanBeResolved(Type t)
{
    CompositionRoot.Get().Resolve(t);
}

This is a slightly more integration-y way for doing checks and leads us towards white-box testing guided by live composition roots. We still should not need live database or some customization of the root for succeeding: even in the second stage, in the constructor stage, we should not need configuration/database/network to be actually present.

Because of wiring checks from the beginning of the article, we already know that dependencies are registered properly. So, in the end, doing this second-stage testing is a way to enforce constructor cleanness across our product.

Conclusion

Taking proper care here has a potential to make a whole class of boring issues disappear. Yay!

All the examples are present in the repository I prepared. Feel free to fork it, clone it, play with it.