Is this possible with Unity (Instead of Castle Win

2019-02-28 11:13发布

问题:

This blog post shows a way to implement auto mocking with Castle Windsor and NSubstitute.

I don't know or use Castle Windsor, but I do use Unity and NSubstitute.

Is there a way to do what he shows using Unity?


Here is relevant content of the post:

First of all, register an ILazyComponentLoader into Windsor:

var c = new WindsorContainer();    
c.Register(Component.For<LazyComponentAutoMocker>());

Then, the implementation of LazyComponentAutoMocker is simply this:

public class LazyComponentAutoMocker : ILazyComponentLoader
{    
  public IRegistration Load(string key, Type service, IDictionary arguments)    
  {    
    return Component.For(service).Instance(Substitute.For(new[] { service }, null));    
  }    
}

And you’re done! Here’s a simple unit test example using only the code from above:

[Test]
public void IDictionary_Add_Invoked()
{
  var dict = c.Resolve<IDictionary>();
  dict.Add(1, 1);
  dict.Received().Add(1, 1);
}

回答1:

With Unity you can write a custom container extension which does the automocking.

Based on this article, you need something like:

EDIT: There was a bug in my implementation sample: see this SO question: NSubstitute and Unity

So the fixed code looks like this:

public class AutoMockingContainerExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        var strategy = new AutoMockingBuilderStrategy(Container);

        Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
    }

    class AutoMockingBuilderStrategy : BuilderStrategy
    {
        private readonly IUnityContainer container;
        private readonly Dictionary<Type, object> substitutes 
           = new Dictionary<Type, object>();

        public AutoMockingBuilderStrategy(IUnityContainer container)
        {
            this.container = container;
        }

        public override void PreBuildUp(IBuilderContext context)
        {
            var key = context.OriginalBuildKey;

            if (key.Type.IsInterface && !container.IsRegistered(key.Type))
            {
                context.Existing = GetOrCreateSubstitute(key.Type);
                context.BuildComplete = true;
            }
        }

        private object GetOrCreateSubstitute(Type type)
        {
            if (substitutes.ContainsKey(type))
                return substitutes[type];

            var substitute = Substitute.For(new[] {type}, null);

            substitutes.Add(type, substitute);

            return substitute;
        }
    }
}

And you can register it when creating your cotainer:

IUnityContainer container = new UnityContainer();
container.AddExtension(new AutoMockingContainerExtension());