调用私有类方法未在构造函数中初始化

时间:2014-04-23 13:03:58

标签: c# unit-testing asp.net-mvc-4 mocking moq

我正在编写一个单元测试,我遇到的一个问题是私有类上的null异常,它不是DI的一部分或者没有在构造函数参数上初始化。有人可以帮帮我吗?这是我的代码。我的问题是如何模拟PPortalSessionVariables类。

控制器:

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

测试类:

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

3 个答案:

答案 0 :(得分:2)

有两件事真的让你头疼:

  1. _ppSessionVariable是私有的,而不是暴露于外界的事实
  2. 假设IPPortalSessionVariables.CurrentChild永远不会返回null,对于接口的所有实现
  3. 如果您解决其中任何一个问题,那么您的问题就会消失。

    1. 公开一个公共setter,允许单元测试显式地将_ppsessionVariable设置为模拟对象。类似的东西:

      public void SetSessionVariable(IPPortalSessionVariables ppsessionVariable)
      {
          _ppsessionVariable = ppsessionVariable;
      }
      
    2. 重构您的代码,以防止_ppsessionVariable.CurrentChild返回null

      最简单的方法可能是在CurrentChild构造函数中将PPortalSessionVariables初始化为Null Object

答案 1 :(得分:2)

您的EducatorController显然与PPortalSessionVariables非常紧密地联系在一起。直到你在控制器单元中new PPortalSessionVariables()的时间进行单独测试是不可能的。 为了解决这个问题,请确保EducatorController取决于抽象IPPortalSessionVariables而不是具体实现。

像其他人已经建议的那样,考虑为IPPortalSessionVariables设置一个公共setter,或继续进行构造函数注入。

答案 2 :(得分:1)

不要理解为什么你不能将它用作通过IoC注入它的任何其他依赖。就Moq而言,你应该模拟该类,但永远不能设置该对象,也许一个解决方法是为该属性创建一个setter并在测试中调用传递mock对象的属性。

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