如何正确模拟我的controllercontext来测试ViewResult.ExecuteResult()?

时间:2011-05-17 02:30:08

标签: c# asp.net-mvc asp.net-mvc-3 integration-testing

我正在尝试创建集成测试,以确保我的视图中没有任何运行时错误。因此,我需要创建一个测试,检查ViewResult.ExecuteResult()是否正常工作,但似乎我遇到了障碍。

我发现this site给了我一个起点,我有以下代码:

    [TestMethod]
    public void RegisterResultExecutes()
    {
        //arrange 
        RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
        AccountController controller = new AccountController
        {
            FormsService = new MockFormsAuthenticationService(),
            MembershipService = new MockMembershipService(),
            Url = new UrlHelper(requestContext)
        };

        var result = controller.Register();
        var sb = new StringBuilder();
        Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
        response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
        {
            sb.Append(y);
        });
        Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
        controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);

        //act 
        result.ExecuteResult(controllerContext.Object);
    }

问题是,当调用result.ExecuteResult()时,我得到以下异常

System.NullReferenceException: Object reference not set to an instance of an object.

System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
MyApp.Tests.Controllers.AccountControllerTest.RegisterResultExecutes() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.Tests\Controllers\AccountControllerTests.cs: line 297

不幸的是,该堆栈跟踪不是很有用,因为我不确定它尝试访问的是null。有没有人对如何为ExecuteResult()创建测试提出任何建议?

2 个答案:

答案 0 :(得分:4)

基于堆栈跟踪,ViewResultBase.ExecuteResult方法中的某些内容会抛出异常。使用反射器,这是该方法的定义:

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    if (string.IsNullOrEmpty(this.ViewName))
    {
        this.ViewName = context.RouteData.GetRequiredString("action");
    }
    ViewEngineResult result = null;
    if (this.View == null)
    {
        result = this.FindView(context);
        this.View = result.View;
    }
    TextWriter output = context.HttpContext.Response.Output;
    ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
    this.View.Render(viewContext, output);
    if (result != null)
    {
        result.ViewEngine.ReleaseView(context, this.View);
    }
}

基于该代码,当代码尝试从上下文访问RouteData属性时(如果视图的名称未明确赋予返回类型),可能会抛出对象引用异常。 / p>

访问HttpContext属性可能会引发异常。我没有充分利用Moq知道它是否可以处理你没有告诉它如何模拟HttpContext属性这一事实,但是你告诉它如何模拟Response属性HttpContext属性的类型,这是我怀疑的另一个区域。

方法中上下文的所有其他用法都将它传递给其他方法,如果这些方法存在问题,那么堆栈跟踪就会显示出来。

最简单的方法是看看我提到的两个问题中的哪一个是问题,我会写一个快速测试从你的模拟中提取这些属性,看看哪一个导致异常。

答案 1 :(得分:1)

我刚刚遇到了同样的问题,并通过设置HttpContext.Current来解决它。

尝试将以下内容添加到您的单元测试代码中:例如

HttpContext.Current = new HttpContext(
    new HttpRequest("", "http://mock", ""),
    new HttpResponse(new StringWriter()));

我发现调试此类问题而不是使用反射器或ILSpy有用的一件事是打开.Net框架代码的调试符号。这样你就可以附加到你的NUnit进程,看看究竟是什么行代码抛出了异常,因此你需要在测试中模拟Mock。

Shawn Burke撰写了一篇精彩的博客文章,详细介绍了如何在此处进行设置:http://blogs.msdn.com/b/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx