我正在编写一个单元测试,我遇到的一个问题是私有类上的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);
}
}
答案 0 :(得分:2)
有两件事真的让你头疼:
_ppSessionVariable
是私有的,而不是暴露于外界的事实IPPortalSessionVariables.CurrentChild
永远不会返回null
,对于接口的所有实现如果您解决其中任何一个问题,那么您的问题就会消失。
公开一个公共setter,允许单元测试显式地将_ppsessionVariable
设置为模拟对象。类似的东西:
public void SetSessionVariable(IPPortalSessionVariables ppsessionVariable)
{
_ppsessionVariable = ppsessionVariable;
}
重构您的代码,以防止_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);
}