模拟控制器以测试区域内的ViewEngine - nullreference和RouteData

时间:2017-08-15 11:23:43

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

我的MVC网站中有一个Area。该区域具有典型的控制器/模型/视图设置。

作为控制器,我有以下代码:

public class DocumentCreatorController : Controller
{
    // GET: Templates/DocumentCreator
    public ActionResult OfferTemplate(BaseDocumentViewModel data)
    {
        return this.Pdf(nameof(OfferTemplate), data, "File.pdf");
    }
}

方法this.Pdf做了几件事,但有趣的是它归结为ViewEngine调用:

var viewResult = ViewEngines.Engines.FindPartialView(controllerContext, partialViewName);

在此,我使用FindPartialViewControllerContext来呼叫PartialViewName。我的PartialViewName来自控制器操作nameof(OfferTemplate)中的OfferTemplate。我认为controllercontext是我的挑战。

我的挑战:

如果我想在单元测试中设置它(使用Moq),我会根据Mocking The RouteData Class in System.Web.Routing for MVC applicationsMocking Asp.net-mvc Controller Context等页面提供以下代码:

[TestMethod]
public void OfferTemplate()
{
    var ctr = SetupControllerWithContext();

}

private static DocumentCreatorController SetupControllerWithContext()
{
    var routeData = new RouteData();
    routeData.Values.Add("controller", "DocumentCreatorController");
    routeData.Values.Add("action", "OfferTemplate");


    var request = new Mock<HttpRequestBase>();
    request.Expect(r => r.HttpMethod).Returns("GET");
    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Expect(c => c.Request).Returns(request.Object);
    var controllerContext = new ControllerContext(mockHttpContext.Object
        , routeData, new Mock<ControllerBase>().Object);

    DocumentCreatorController ctr = new DocumentCreatorController();
    ctr.ControllerContext = controllerContext;
    return ctr;
}

出现以下错误:

  

Eesy.Websites.Api.Tests.Controllers.DocumentCreatorControllerTest.OfferTemplate   抛出异常:       System.NullReferenceException:未将对象引用设置为对象的实例。

这个我不明白。

我的文件夹设置:

enter image description here

调用FindPartialView时调试ControllerContext上的图像:

enter image description here

有人有想法吗? 是因为我设置了RouteData错了吗?

1 个答案:

答案 0 :(得分:1)

您正在尝试模拟和测试框架代码。摘要将功能输出到您控制的代码中,以便在需要时可以单独进行测试。

目前,操作和扩展控制器与外部第三方依赖关系紧密耦合。如果目标是单独测试控制器操作流程,则建议抽象出第三方PDF生成,以便可以模拟它以便于测试。

public interface IDocumentService {
    ActionResult ToPdf(Controller arg1, string arg2, object arg3, string arg4);
}

控制器将通过构造函数注入显式依赖于此抽象。

public class DocumentCreatorController : Controller {
    private readonly IDocumentService render;

    DocumentCreatorController(IDocumentService render) {
        this.render = render;
    }

    // GET: Templates/DocumentCreator
    public ActionResult OfferTemplate(BaseDocumentViewModel data) {
        return render.ToPdf(this, nameof(OfferTemplate), data, "File.pdf");
    }
}

所以现在要测试控制器的pdf生成过程,你只需要模拟你的抽象。

[TestMethod]
public void OfferTemplate() {
    //Arrange    
    var serviceMock = new Mock<IDocumentService>();
    //...setup mock for use case

    var controller = new DocumentCreatorController(serviceMock.Object);
    var data = new BaseDocumentViewModel {
        //...
    };

    //Act
    var actual = controller.OfferTemplate(data);

    //Assert
    //...assert behavior
}

该服务的实际实现将封装实际功能,并将与依赖注入容器一起注册以及抽象。

要测试实际生成,您需要进行集成测试,这是另一个主题。