Reputation: 5467
I have a simple contoller of a standard MVC 5 web application. I installed the MVC 5 Ninject nuget package. I created a constructor and the interface that I have defined is injected. If I create a public property this is also injected.
Now when I create a unittest project and want to test the mvc controller. How can I do that? I have to create a kernel. The kernel is created and if I get an instance of my IPersonComponent it is created. But why doesn't my property not get injected?
I created two options and both I get nullreference exception...
Interface:
public interface IPersonComponent
{
void DoSomething();
}
Implementation:
public class PersonComponent : IPersonComponent
{ public void DoSomething()
{
throw new NotImplementedException("The method is fired but not implemented");
}
}
TestInitialize:
private IKernel _kernel;
[TestInitialize()]
public void Initialize()
{
_kernel = new StandardKernel();
}
Option 1
Controller that is working great:
public class HomeController : Controller
{
private readonly IPersonComponent _component;
public HomeController(IPersonComponent component)
{
_component = component;
}
}
Unittest fails because PersonComponent is null:
[TestClass]
public class HomeControllerTest
{
[Inject]
public IPersonComponent PersonComponent { get; set; }
[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void Index()
{
// Arrange
HomeController controller = new HomeController(PersonComponent);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
}
Option 2
Controller:
public class HomeController : Controller
{
[Inject]
public IPersonComponent PersonComponent { get; set; }
public HomeController()
{
}
public ActionResult Index()
{
PersonComponent.DoSomething();
return View();
}
}
Test:
[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
This works: But why does it work? Why isn't it injected automatically?
[TestMethod]
public void TestDoSomething()
{
var kernel = new StandardKernel();
var comp = kernel.Get<IPersonComponent>();
comp.DoSomething();
}
Edit based on the answer:
Option 1
[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void Index()
{
_kernel.Inject(this);
// Arrange
HomeController controller = new HomeController(PersonComponent);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
Option 2
[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void Index()
{
// Arrange
HomeController controller = _kernel.Get<HomeController>();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
Upvotes: 2
Views: 1868
Reputation: 156544
In option 1: MsTest does not invoke Ninject to tell it to inject services into your objects.
In option 2: You are creating the controller manually (new HomeController()
), and you're not telling Ninject to inject services into your objects.
Although much of what Ninject does seems "magical," it still has to be invoked in order to be able to inject values into your components. ASP.NET MVC has a mechanism that Ninject ties itself into, so that construction of controllers is handled by Ninject automatically. This gives you the impression that Ninject is able to inject values into anything without effort. But MSTest does not have a similar mechanism, so you have to do some of this work manually.
If you have access to the ninject kernel that's been configured, you can say:
HomeController controller = kernel.Get<HomeController>();
Or, in option 1, you can say:
public HomeControllerTest()
{
kernel.Inject(this); // populate my [Inject] properties
}
I should mention that if you're injecting dependencies based on real bindings, you're not really writing "unit tests," but rather "automated acceptance tests." If you want to test the behavior of a controller action independent of its dependencies, learn how to "mock" its dependencies instead.
Upvotes: 2