3

Follow up to an older question here. Say I have a registration like the following:

container.Register(typeof(IHandleCommand<>), _handlerAssemblies, Lifestyle.Transient);
container.RegisterDecorator(typeof(IHandleCommand<>),
  typeof(MetricsCommandHandlerWrapper<>), Lifestyle.Singleton);

Where the MetricsCommandHandlerWrapper is defined like so:

public class MetricsCommandHandlerWrapper<T> : IHandleCommand<T> where T: ICommand
{
    private readonly ICollectMetrics _metrics;
    private readonly Func<IHandleCommand<T>> _handlerFactory;

    public MetricsCommandHandlerWrapper(ICollectMetrics metrics,
      Func<IHandleCommand<T>> handlerFactory)
    {
      _metrics = metrics;
      _handlerFactory = handlerFactory;
    }

    public async Task HandleAsync(T command)
    {
        // code to record metrics around command handling which eventually invokes
        await _handlerFactory().HandleAsync(command).ConfigureAwait(false);
    }
}

How can I write a unit test that asserts the actual decoratee handlers are registered with Transient lifestyle?

I have tried composing the root and inspecting the registration for a closed IHandleCommand<FakeCommand> type, which reveals an ImplementationType of MetricsCommandHandlerWrapper<FakeCommand> as expected. Invoking GetRelationships() on that registration reveals its 2 dependencies, ICollectMetrics and the one I am interested in, the Func<IHandleCommand<FakeCommand>> factory delegate, which is registered as a Singleton. However invoking .Dependency.GetInstance() on that factory delegate throws an exception that the instance producer returned null.

Is there any way to assert that the inner decoratee is registered as Transient, and if so, how?

Community
  • 1
  • 1
danludwig
  • 46,965
  • 25
  • 159
  • 237

1 Answers1

3

The use of the Func<T> delays the building of the object graph, and from perspective of the diagnostic system, the graphs stops at that point. So, it's not possible to do this analysis.

Instead of completely relying on Simple Injector's internals however, you can also choose to make some minor changing in your application to allow testing decorators.

What you can do is implement an IDecorator abstraction on your decorators:

public interface IDecorator {
    object Decoratee { get; }
}

Now each decorator can implement this interface. For the MetricsCommandHandlerWrapper<T>, it might look as follows:

public class MetricsCommandHandlerWrapper<T> : IHandleCommand<T>, IDecorator where T: ICommand
{
    private readonly ICollectMetrics _metrics;
    private readonly Func<IHandleCommand<T>> _handlerFactory;

    public MetricsCommandHandlerWrapper(ICollectMetrics metrics,
      Func<IHandleCommand<T>> handlerFactory) {
      _metrics = metrics;
      _handlerFactory = handlerFactory;
    }

    public object Decoratee { get { return _handlerFactory(); }

    public async Task HandleAsync(T command) { ... }
}

On top of the IDecorator interface, you can define a simple extension method:

public static IEnumerable<Type> GetDecoratorChain(this IDecorator decorator) {
    while (decorator != null) {
        yield return decorator.GetType();
        decorator = decorator.Decoratee as IDecorator;
    }
}

Inside your unit test you can now resolve a handler and ask for the list of applied decorators. Using this list you can verify whether decorators are applied in the correct order.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • So to be clear, the answers to the OP are "you cannot" and "no"? (in reference to asserting the trainsient lifestyle) – danludwig Dec 04 '15 at 19:29
  • @danludwig: I'm sorry, I missed that part of the question. Yes you can actually test this. You test this by preventing the `MetricsCommandHandlerWrapper` from being registered during that test. When you do this, you can test whether the handlers are transient. But to be honest, I'm unsure why you want to test whether your handlers are transient. You registered them as transient explicitly using `container.Register(typeof(IHandleCommand<>), _handlerAssemblies, Lifestyle.Transient)`, which is very declarative. Why do you want to test this? – Steven Dec 04 '15 at 23:07
  • Had I omitted the last argument (which is optional), it would not be as declarative. It just seems to me like lifestyles should be assertable from tests. There are probably other ways to test it using fake handlers as well, but it's good to know which of these things can't be determined without resolving an instance (or making the test skip the decorator registrations, which I don't like). – danludwig Dec 04 '15 at 23:15
  • @danludwig At this moment I don't think there is a way to get to those registrations. That was the reason we had a discussion about this in the past. So we might address this in the future, but don't bet on this happening soon. – Steven Dec 05 '15 at 22:12
  • 1
    Not a problem. I was able to assert the transient registration using fake handlers. I may post that solution here if it turns out anyone else is interested. – danludwig Dec 05 '15 at 22:14
  • This is a nice and useful way to test the order of your decorators. @Steven, in your example you're using a C# 6.0 feature *expression bodied property* (`public object Decoratee => ...;`). This might be confusion for the ones that haven't used C# 6.0 yet. – QuantumHive Dec 15 '15 at 14:45