Reputation: 696
I am currently trying to learn DI & Mocking, with Unity and NSubstitute. I am also using an automocking extension taken from this question: Is this possible with Unity (Instead of Castle Windsor)?
In my unit test below I am trying to set an NSubstitute return value of 10 from my method Add(). However when debugging through the controller call the assigned value is the default 0 rather than the expected 10. The proxy does not seem to be intercepting the method call.
I suspect this is caused by not registering my Types/container correctly, anybody able to point me in the right direction?
[TestFixture]
public class ApplicationControllerTests
{
private IUnityContainer _container;
private ApplicationController _controller;
private ISampleService _sampleService;
[SetUp]
public void SetUp()
{
_container = new UnityContainer().AddExtension(new AutoMockingContainerExtension());
_controller = _container.Resolve<ApplicationController>();
_sampleService = _container.Resolve<ISampleService>();
}
[Test]
public void TestSampleService()
{
// This line is not working
_sampleService.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(10);
var result = _controller.Index();
_sampleService.Received().Add(Arg.Any<int>(), Arg.Any<int>());
}
}
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;
public AutoMockingBuilderStrategy(IUnityContainer container)
{
_container = container;
}
public override void PreBuildUp(IBuilderContext context)
{
var key = context.OriginalBuildKey;
if (key.Type.IsInterface && !_container.IsRegistered(key.Type))
context.Existing = CreateSubstitute(key.Type);
}
private static object CreateSubstitute(Type type)
{
return Substitute.For(new[] { type }, null);
}
}
}
And my controller code
public class ApplicationController : BaseController
{
private readonly ISampleService _sampleService;
public ApplicationController(ISampleService sampleService)
{
_sampleService = sampleService;
}
public ActionResult Index()
{
var result = _sampleService.Add(2, 3);
// result is 0, expected 10 ??
return View();
}
}
public interface ISampleService
{
int Add(int first, int second);
}
public class SampleService : ISampleService
{
public int Add(int first, int second)
{
return first + second;
}
}
Upvotes: 2
Views: 3317
Reputation: 4573
I suspect that you are dealing with two different instances of ISampleService, created in lines 2 and 3 of Setup, respectively. Could you, for test, make the _sampleServie field public and in the third Setup() line try
_sampleService = _controller._sampleService;
Upvotes: 1
Reputation: 139778
Actually Tormod is right the problem is that the AutoMockingBuilderStrategy
returns a different mock instance every time when somebody requests an ISampleService
form the container.
So there is a bug in my original implementation namely the AutoMockingBuilderStrategy
doesn't store the created mocks:
Here is a fixed version:
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;
}
}
}
Upvotes: 4