【问题标题】:ASP.NET MVC: Unit testing controllers that use UrlHelperASP.NET MVC:使用 UrlHelper 的单元测试控制器
【发布时间】:2009-03-23 17:43:29
【问题描述】:

我的一个控制器动作,一个在 Ajax 请求中被调用的动作,正在向客户端返回一个 URL,以便它可以进行重定向。我正在使用Url.RouteUrl(..),但在我的单元测试期间,由于Controller.Url 参数未预先填充,因此失败。

我尝试了很多东西,其中包括尝试存根 UrlHelper(失败),手动创建一个带有 RequestContextUrlHelper,它有一个存根 HttpContextBase(在 RouteCollection.GetUrlWithApplicationPath 调用中失败) )。

我在 Google 上进行了搜索,但几乎没有找到关于该主题的任何内容。我在控制器操作中使用Url.RouteUrl 做了一些非常愚蠢的事情吗?有没有更简单的方法?

更糟糕的是,我希望能够在我的单元测试中测试返回的 URL - 实际上我只是想知道它正在重定向到正确的路由,但是因为我正在返回一个 URL而不是路由,我想控制解析的 URL(例如,通过使用存根 RouteCollection) - 但我很乐意让我的测试开始通过。

【问题讨论】:

    标签: asp.net-mvc unit-testing urlhelper


    【解决方案1】:

    这是我的测试之一(xUnit + Moq),仅针对类似情况(在控制器中使用 Url.RouteUrl)

    希望这会有所帮助:

    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    
    var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
    request.SetupGet(x => x.ApplicationPath).Returns("/");
    request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
    request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());
    
    var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
    response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");
    
    var context = new Mock<HttpContextBase>(MockBehavior.Strict);
    context.SetupGet(x => x.Request).Returns(request.Object);
    context.SetupGet(x => x.Response).Returns(response.Object);
    
    var controller = new LinkbackController(dbF.Object);
    controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
    controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
    

    【讨论】:

    • 暂时我采用了一个解决方案,我抽象出对 UrlHelper 的调用,以便我可以拦截它们。不过,感谢您的 sn-p,它将为我节省大量时间来弄清楚如何正确模拟 Request/Response/ControllerContext。
    • 感谢@eu-ge-ne 的回答,它也帮助了我很多。我已经包含了一些更多的起订量设置来使用 UpdateModel 使用的 formcollection 参数
    • +1 非常好。虽然提示:我将其用作 MockHelper 并将 ApplyAppPathModifier 的 response.Setup 更改为: response.Setup(x => x.ApplyAppPathModifier(Moq.It.IsAny())).Returns((String url ) => 网址);这很难看,但我以 url 编码形式返回序列化对象,而不是对返回的值进行硬编码。
    • 这部分对我有用。任何想法为什么我得到 Controller/ 而不是 Controller/Action?我的测试失败了,因为它们并不完全相同,但我注册了相同的路由值。很奇怪……
    • ApplyAppPathModifier 部分是 UrlHelper 的关键位
    【解决方案2】:

    来自 eu-ge-ne 的修改实现。这个返回基于应用程序中定义的路由生成的链接。 eu-ge-ne 的示例总是返回一个固定的响应。下面的方法将允许您测试正确的操作/控制器和路由信息是否正在传递到 UrlHelper - 如果您正在测试对 UrlHelper 的调用,这就是您想要的。

    var context = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    var response = new Mock<HttpResponseBase>();
    var session = new Mock<HttpSessionStateBase>();
    var server = new Mock<HttpServerUtilityBase>();
    
    context.Setup(ctx => ctx.Request).Returns(request.Object);
    context.Setup(ctx => ctx.Response).Returns(response.Object);
    context.Setup(ctx => ctx.Session).Returns(session.Object);
    context.Setup(ctx => ctx.Server).Returns(server.Object);
    
    request.SetupGet(x => x.ApplicationPath).Returns("/");
    request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
    request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());
    
    response.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns<string>(x => x);
    
    context.SetupGet(x => x.Request).Returns(request.Object);
    context.SetupGet(x => x.Response).Returns(response.Object);
    
    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    var helper = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
    

    【讨论】:

      【解决方案3】:

      如果你想模拟 HttpContextBase 类,这篇文章可能会很有用。

      http://www.hanselman.com/blog/ASPNETMVCSessionAtMix08TDDAndMvcMockHelpers.aspx

      【讨论】:

      • 酷,这对我有帮助,尽管我不得不在 FakeHttpContext 方法中添加一些额外的代码来阻止助手爆炸:context.Setup(ctx => ctx.Request.ApplicationPath).Returns( "/防爆");我还重构了代码,使其使用更新的 Setup() 语法。谢谢。
      【解决方案4】:

      以@eu-ge-ne 的回答为基础,这对我帮助很大:

      我有一个执行重定向的 ActionResult 以及一个带有 FormCollection 参数的 UpdateModel 调用。为了让 UpdateModel() 工作,我必须将它添加到我的 Mocked HttpRequestBase:

      FormCollection collection = new FormCollection();
      collection["KeyName"] = "KeyValue";
      
      request.Setup(x => x.Form).Returns(collection);
      request.Setup(x => x.QueryString).Returns(new NameValueCollection());
      

      要测试重定向的 URL 是否正确,您可以执行以下操作:

      RedirectResult result = controller.ActionName(modelToSubmit, collection) as RedirectResult;
      Assert.AreEqual("/Expected/URL", result.Url);
      

      【讨论】:

        【解决方案5】:

        http://kbochevski.blogspot.com/2010/06/unit-testing-mvcnet.html 讨论如何使用单元测试覆盖所有层。它使用 Rhino 和 MVC.Contrib 进行控制器测试。 google 上的源码可能会有很大帮助。

        【讨论】:

          猜你喜欢
          • 2011-08-22
          • 1970-01-01
          • 2016-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-30
          相关资源
          最近更新 更多