Question

I am writing a unit test and one of the issues that I encounter is null exception on a private class that is not part of DI or not being initialized on constructor parameters. Anyone can help me? Here's my code. My problem is that how to mock PPortalSessionVariables class.

Controller:

public class EducatorController : BaseController
{
    //Note: PPortalSessionVariables class should NOT be part of IOC
    private readonly IPPortalSessionVariables _ppsessionVariable = new PPortalSessionVariables();
    private readonly IEducatorService _educatorService;

    public EducatorController(IEducatorService educatorService)
    {
        _educatorService = educatorService;
    }

    public ActionResult Index()
    {
        //during test null exception occurs on _ppsessionVariable.CurrentChild.Id
        var model = _educatorService.GetEducatorsForChild(Convert.ToInt64(_ppsessionVariable.CurrentChild.Id));
        return View(model);
    }
}

Test Class:

[TestClass]
public class EducatorControllerTests
{
    public EducatorController CreateController(Mock<IEducatorService> educatorService = null)
    {
        educatorService = educatorService ?? new Mock<IEducatorService>();
        HttpContext.Current = HttpMockHelpers.FakeHttpContextCurrent();
        var controller = new EducatorController(educatorService.Object);
        controller.SetFakeControllerContext("?site=2");
        return controller;
    }

    [TestMethod]
    public void Index_Get_ReturnIndexView()
    {
        var ppsessionVariable = new Mock<IPPortalSessionVariables>();
        var controller = CreateController();
        var child = new ChildModel();
        child.Id = 0;
        ppsessionVariable.Setup(x => x.CurrentChild).Returns(child);
        var result = controller.Index() as ViewResult;
        Assert.IsNotNull(result);
    }
}
Was it helpful?

Solution 3

Don't understand why you just don't use it as any other dependency injecting it via IoC. As far as Moq works you should mock that class but never will be able to set that object, maybe a workaround is create a setter for that property and call the property in your test passing the mock object.

EducatorController

public void SetPortalSession(IPPortalSessionVariables portal)
{
   _ppsessionVariable = portal;
}

EducatorControllerTests

 [TestMethod]
 public void Index_Get_ReturnIndexView()
 {
     var ppsessionVariable = new Mock<IPPortalSessionVariables>();
     var controller = CreateController();
     controller.SetPortalSession(ppsessionVariable.object);
     var child = new ChildModel();
     child.Id = 0;
     ppsessionVariable.Setup(x => x.CurrentChild).Returns(child);
     var result = controller.Index() as ViewResult;
     Assert.IsNotNull(result);
  }

OTHER TIPS

There are two things that are really causing this headache for you:

  1. The fact that _ppSessionVariable is private, and not exposed to the outside world
  2. There is an assumption that IPPortalSessionVariables.CurrentChild should never return null, for all implementations of the interface

If you address either of these points, then your problem goes away.

  1. Expose a public setter to allow the unit test to explicitly set the _ppsessionVariable to the mock object. Something like:

    public void SetSessionVariable(IPPortalSessionVariables ppsessionVariable)
    {
        _ppsessionVariable = ppsessionVariable;
    }
    
  2. Refactor your code to prevent _ppsessionVariable.CurrentChild from returning null.

    The simplest thing would probably be to initialize CurrentChild to a Null Object, in the PPortalSessionVariables constructor.

Your EducatorController is clearly very tightly coupled with PPortalSessionVariables. Till the time you have new PPortalSessionVariables() in the controller unit testing it in isolation will not be possible. In order to fix this, make sure the EducatorController depends on an abstraction IPPortalSessionVariables instead of the concrete implementation.

Like others have already suggested, consider having a public setter for IPPortalSessionVariables or go ahead with a constructor injection.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top