Unity and auto-mocking with NSubstitute (or something else)

1.1k views Asked by At

My question derives from this question: Is this possible with Unity (Instead of Castle Windsor)?

Here is the class from the answer:

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;
    }
}

The posted solution there works well for a SINGLE object. If you see the solution posted, it will return the same object everytime I call Resolve

This is fine in a test case like the following:

var myObjectWithInnerMockedObjects = UnityContainer.Resolve<ISomeInterface>();
var internalAutoMockedObject = UnityContainer.Resolve<IInnerInterface>();

The solution posted in the said question works well for above.

The above code creates an object via Unity and tries to resolve the constructor arguments and if the type is not mapped in unity config, returns a mock via NSubstitute.

So effectively the chain could be:

- ActualObject
    - InnerActualObjectDependency
        - MockedDependency (since this was not mapped in Unity, a mock was created)

Problem is if I create 2 of such objects, the mocks point to the same object.

If I remove the CONTAINS check, in method GetOrCreateSubstitute(), then I get a new mock every time... But then how do I access a particular object's mock to set expectations on it? :-(

I hope I am clear with the question!!

1

There are 1 answers

0
Ramanpreet Singh On BEST ANSWER

We had an internal team discussion and I understood better that mocks are meant to be singleton. By design we do not want a different mock object for every created object.

A mock is for a class and not for methods. So the following pseudo-code would be the perfect way to achieve what I am doing.

var obj1 = Resolve<IMyInterface>();
var obj2 = Resolve<IMyInterface>();
var innerDependency = Resolve<IInnerType>(); // Returns the same object that is shared by above 2 objs

innerDependency.SetExpection(Some expectation);
obj1.PerformAction();
innerDependency.Assert();

innerDependency.SetExpection(Some *different* expectation);
obj2.PerformAction();
innerDependency.Assert();

So The very pretext of the question is faulty. We do not have such flexibility because we do not want to!