Search code examples
c#dependency-injectionsimple-injector

Simple Injector: How to test registrations?


I'm using Simple Injector and using pretty advances registrations techniques and may use some of the extension points for even more advances registrations (see motivation below). This has raised the need to write unit tests that verify the container is bootstrapped correctly. For example, assumes types A1 & A2 have dependency on service B which is implemented by both C1 and C2, such that A1 should be injected with C1 and A2 should be injected with C2. I'd like to write a test that verifies that A1 is injected with C1 when resolved and A2 is injected with C2. While I could expose public properties for all injected services from A1 and A2 to verify their types in a test, I rather not do it just for the sake of testing. Is there a simple API to test how an object graph would be resolved, perhaps something based on the Diagnostics API which seems to do the same for the purpose of debugging views?

Motivation: the motivation behind the advances registrations is implementing design and architectural decisions by substituting/decorating injected services based on the injection context. This has served me really well in keeping the application code simple and testable, putting all the "if-then-else" complexity in the container registrations level, and keeping application code truly SOLID while making changes almost solely to the container registrations. The idea is inspired by .NET Junkie's posts about commands & queries and the usage of decorators, and the capability to apply certain decorators based on compile-time or run-time context, which is really powerful and useful for a SOLID design.


Solution

  • Is there a simple API to test how an object graph would be resolved, perhaps something based on the Diagnostics API which seems to do the same for the purpose of debugging views?

    Yes there are several ways to do this. Two options that come to mind are the VisualizeObjectGraph and GetRelationships methods on InstanceProducer. Both allow visualizing object graphs. GetRelationships gives you a structured graph that you can iterate over, while VisualizeObjectGraph returns the object graph in a string format, close resembling C#.

    Example:

    var container = new Container();
    
    // Do registrations
    
    // You need to verify to get the correct output of those methods
    container.Verify();
    
    var r = container.GetRegistration(typeof(A1));
    
    Console.WriteLine(r.VisualizeObjectGraph());
    

    This outputs something like

    A1(
        FileLogger(
            Dependency1(),
            Dependency2()),
        C1(
            Dependency1(),
            SomeService()));
    

    When using the GetRelationships() you can recursively iterate through the graph and check if the given type is a (sub) dependency of the registration.

    Example:

    // Useful extension method
    private static IEnumerable<InstanceProducer> GetDependencies(this InstanceProducer p) {
        foreach (var r in p.GetRelationships()) {
            yield return r.Dependency;
            foreach (var d in r.Dependency.GetDependencies()) {
                yield return d;
            }
        }
    }
    
    var container = new Container();
    
    // Do registrations
    
    // You need to verify to get the correct output of those methods
    container.Verify();
    
    var deps = container.GetRegistration(typeof(A1)).GetDependencies();
    
    Assert.IsTrue(deps.Any(p => p.Registration.ImplementationType == typeof(C1)));
    

    Do note though that I think you should limit these kinds of registration checks as much as possible, because:

    • Registrations should be expressive enough that writing tests would only repeat what you already defined in the composition root.
    • Wider integration tests should already cover most of these scenarios at a functional level.
    • Simple Injector's verification and diagnostic abilities will spot common misconfigurations for you.