LockTar
LockTar

Reputation: 5467

Property inject in unittest with ninject

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

Answers (1)

StriplingWarrior
StriplingWarrior

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

Related Questions