【问题标题】:How to mock a DisplayMode in ControllerContext For Unit Test c#如何在 ControllerContext 中模拟 DisplayMode 以进行单元测试 c#
【发布时间】:2016-11-24 14:50:17
【问题描述】:

我想在我的控制器中测试一个操作,该操作使用 controllerContext 作为参数来生成基于第 3 部分库“Rotativa”的 pdf 文档。
下面是动作(函数)的实现:

public ActionResult DetailsPrint(int? id)
{
    var a = new ViewAsPdf();
    a.ViewName = "../Ops/_2A1/Details";
    a.Model =UnitOfWork._2A1s.Get(id.Value);
    var pdfBytes = a.BuildPdf(ControllerContext);

    // return ActionResult
    MemoryStream ms = new MemoryStream(pdfBytes);
    return new FileStreamResult(ms, "application/pdf");

}

这就是我尝试进行单元测试的方式:

  • 构造函数

    public _2A1ControllerTest()
    {
        _mockRepository = new Mock<I2A1Repository>();
        var mockUoW = new Mock<IUnitOfWork>();
        _mockHttpContext = new Mock<HttpContextBase>();
        _mockRequest = new Mock<HttpRequestBase>();
        _mockDisplayModeContext = new Mock<IDisplayMode>();
        mockUoW.SetupGet(u => u._2A1s).Returns(_mockRepository.Object);
        _mockHttpContext.SetupGet(x => x.Request).Returns(_mockRequest.Object);
        _controller = new _2A1Controller(mockUoW.Object);
        _controller.MockCurrentUser("test.admin");
        _controller.ControllerContext = new ControllerContext(_mockHttpContext.Object, new System.Web.Routing.RouteData(), _controller);
    }
    
  • 测试功能

    [TestMethod]
    public void DetailsPrint_shouldPrint()
    {
        var result = _controller.DetailsPrint(1) as ActionResult;
        result.Should().BeOfType<ActionResult>();
    }
    

    当我执行测试时,我得到以下错误:

测试名称:DetailsPrint_shouldPrint 测试全名:OPSReviewTest._2A1ControllerTest.DetailsPrint_shouldPrint 测试源:C:\inetpub\wwwroot\OpsReview\OPSReviewTest\Controllers\Api_2A1ControllerTest.cs:第 46 行 测试结果:失败 测试时间:0:04:39,3039007 结果堆栈跟踪:
在 System.Web.WebPages.DisplayModeProvider.GetDisplayMode(HttpContextBase 上下文) 在 System.Web.Mvc.ControllerContext.get_DisplayMode() 结果消息: 测试方法 OPSReviewTest._2A1ControllerTest.DetailsPrint_shouldPrint 抛出异常: System.NullReferenceException:对象引用未设置为对象的实例。

任何帮助或建议,谢谢。

【问题讨论】:

  • 你到底想在方法中测试什么?

标签: c# unit-testing moq moq-3 controllercontext


【解决方案1】:

您正在尝试对不属于您的代码进行单元测试? (耻辱,[钟声],耻辱......)

如果目标是单独测试控制器操作流,则建议抽象出第 3 方 PDF 生成,以便对其进行模拟以便于测试。

public interface IViewAsPdfWrapper {
    string ViewName { get; set; }
    object Model { get; set; }
    byte[] BuildPdf(ControllerContext context);
}

public class ViewAsPdfWrapper : IViewAsPdfWrapper {
    private readonly ViewAsPdf view;
    public ViewAsPdfWrapper() {
        view = new ViewAsPdf();
    }
    public string ViewName { get; set; }
    public object Model { get; set; }
    public byte[] BuildPdf(ControllerContext context) {
        view.ViewName = ViewName;
        view.Model = Model;
        return view.BuildPdf(context);
    }
}

现在可以将抽象注入到控制器中,以便根据需要为每个请求操作使用。

public class _2A1Controller : Controller {
    private readonly IUnitOfWork UnitOfWork;
    private readonly IViewAsPdfWrapper viewAsPdf;

    public _2A1Controller(IUnitOfWork uow, IViewAsPdfWrapper viewAsPdf) {
        this.UnitOfWork = uow;
        this.viewAsPdf = viewAsPdf;
    }

    public ActionResult DetailsPrint(int? id) {
        var a = viewAsPdf;
        a.ViewName = "../Ops/_2A1/Details";
        a.Model = UnitOfWork._2A1s.Get(id.Value);
        var pdfBytes = a.BuildPdf(ControllerContext);

        // return ActionResult
        MemoryStream ms = new MemoryStream(pdfBytes);
        return new FileStreamResult(ms, "application/pdf");
    }

}

现在单元测试可以安全地模拟第 3 方功能

public _2A1ControllerTest() {    
    _mockRepository = new Mock<I2A1Repository>();
    var mockUoW = new Mock<IUnitOfWork>();
    mockUoW.SetupGet(u => u._2A1s).Returns(_mockRepository.Object);

    var mockViewAsPdf = new Mock<IViewAsPdfWrapper>();
    mockViewAsPdf.Setup(m => m.BuildPdf(It.IsAny<ControllerContext>()))
        .Returns(() => new byte[0]);

    _mockRequest = new Mock<HttpRequestBase>();
    _mockHttpContext = new Mock<HttpContextBase>();
    _mockHttpContext.SetupGet(x => x.Request).Returns(_mockRequest.Object);

    _controller = new _2A1Controller(mockUoW.Object, mockViewAsPdf.Object);
    _controller.MockCurrentUser("test.admin");
    _controller.ControllerContext = new ControllerContext(_mockHttpContext.Object, new System.Web.Routing.RouteData(), _controller);

}

假设使用 FluentAssertions,测试方法应该如下所示(双关语:))

[TestMethod]
public void DetailsPrint_shouldPrint() {
    var result = _controller.DetailsPrint(1) as ActionResult;
    result.Should()
        .NotBeNull()
        .And
        .BeAssignableTo<ActionResult>();
}

最后,不要忘记在生产环境中将接口及其实现注册到您的 DI 容器中。

【讨论】:

  • 首先,感谢您帮助我,为我节省了大量时间和搜索,我真的很感激。对于您的问题,这是我自己的代码,也是我第一次使用模拟和模拟第 3 部分库。你是这方面的学者。
  • 当我说自己的代码时,我指的是第 3 方库。这是一句谚语,本来是开玩笑的:)。第 3 方库提供商应该在发布之前测试他们的代码。所以尝试测试它是在浪费你的时间。通过将其包装在您拥有的抽象中,您就可以控制模拟它并以更大的灵活性进行测试。
  • 嗯,好的,感谢您的所有建议和您的时间。
【解决方案2】:

您忘记将DsiplayMode 属性分配给ControllerContext 添加此:

_controller.ControllerContext.DisplayMode=_mockDisplayModeContext.Object;

【讨论】:

  • 感谢@esiprogrammer 的回答,但没有成功,
  • @ayyoub 你是什么意思它没有工作。你在同一行还有同样的例外吗?
  • 是的,我仍然有同样的异常,我昨天花了 4 个小时没有结果,然后我把它放在 stackoverflow 中,希望以前有人遇到过同样的问题。感谢您的互动。
猜你喜欢
  • 2013-11-18
  • 2010-11-08
  • 2013-03-25
  • 2018-04-04
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多