UrlHelper 的 RouteUrl 在测试中返回空字符串

发布于 2024-09-12 17:19:26 字数 2625 浏览 5 评论 0原文

我遇到一个问题,UrlHelper 的 RouteUrl 方法在我的测试中运行时仅返回空字符串,但在真正的 HttpContext 中执行时可以正常运行。然而,它正在寻找路线 - 因为如果我尝试解析尚未定义的路线名称,我确实会正确地得到异常。

我使用 Scott Hanselman/Kzu 提供的 代码来嘲笑 HttpContext 和朋友 并添加将应用程序的路由引导至所需的代码 为了减少我的情况中的

变量数量,我编写了一个简单的测试:

[Test]
public void UrlHelperReturnsCorrectUrl()
{
  var controller = new MyController();
  controller.SetFakeControllerContext().LoadUrlHelper();

  Assert.AreEqual("My/Route/Path", controller.Url.RouteUrl("MyRoute"));
}

有趣的是,直接访问 RouteCollection 并使用 VirtualPath 确实有效:

[Test]
public void GetVirtualPathReturnsCorrectUrl()
{
    var controller = new AccountController();
    controller.SetFakeControllerContext().LoadUrlHelper();
    Assert.AreEqual("My/Route/Path", 
               Controller.Url.RouteCollection["MyRoute"]
               .GetVirtualPath(
                   controller.Url.RequestContext,
                   new RouteValueDictionary())
               .VirtualPath);
}

作为参考,这是我对 LoadUrlHelper 扩展方法的实现:

public static Controller LoadUrlHelper(this Controller controller)
{
    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    controller.Url = new UrlHelper(
                        controller.ControllerContext.RequestContext, 
                        routes);
    return controller;
}

这是我的我的应用程序的 Global.asax 中定义的路线:

routes.MapRoute(
   "MyRoute", "My/Route/Path",
   new {controller = "Home", action = "Index"});

有人遇到过这个吗?我错过了什么吗?

编辑:

我已经跟踪了 MVC 代码,直到它将路由处理交给 System.Routing 并发现了一些非常有趣的东西。 MVC 最终运行来查找所需 URL(当然是压缩的)的代码返回一个空字符串:

Controller.Url.RouteCollection.GetVirtualPath(
          Controller.Url.RequestContext, 
          "MyRoute", new RouteValueDictionary()).VirtualPath;

而一个极其相似的变体返回预期的字符串:

Controller.Url.RouteCollection["MyRoute"].GetVirtualPath(
          Controller.Url.RequestContext, 
          new RouteValueDictionary()).VirtualPath;

我似乎无法在底层代码中进一步查看实际内容这里发生的情况有所不同,但我认为这可能会帮助别人理解我缺少什么设置。 (我还不会大喊错误,因为事实上 UrlHelpers 在真正的 HttpContext 中确实可以工作)

I am having an issue where UrlHelper's RouteUrl method only returns an empty string when run in my tests, though function properly when executing in the real HttpContext. It is, however, finding the route - as I do properly get an exception if I try to resolve a route name which has not been defined.

I have mocked the HttpContext and friends using the code provided by Scott Hanselman/Kzu and added the code needed to bootstrap the Application's Routes into the mocked instance

To reduce the number of variables in my situation, I've written a simple test:

[Test]
public void UrlHelperReturnsCorrectUrl()
{
  var controller = new MyController();
  controller.SetFakeControllerContext().LoadUrlHelper();

  Assert.AreEqual("My/Route/Path", controller.Url.RouteUrl("MyRoute"));
}

Interestingly enough, accessing the RouteCollection directly and using VirtualPath does work:

[Test]
public void GetVirtualPathReturnsCorrectUrl()
{
    var controller = new AccountController();
    controller.SetFakeControllerContext().LoadUrlHelper();
    Assert.AreEqual("My/Route/Path", 
               Controller.Url.RouteCollection["MyRoute"]
               .GetVirtualPath(
                   controller.Url.RequestContext,
                   new RouteValueDictionary())
               .VirtualPath);
}

For reference, Here is my implementation of the LoadUrlHelper extension method:

public static Controller LoadUrlHelper(this Controller controller)
{
    var routes = new RouteCollection();
    MvcApplication.RegisterRoutes(routes);
    controller.Url = new UrlHelper(
                        controller.ControllerContext.RequestContext, 
                        routes);
    return controller;
}

And here is my route as defined in my application's Global.asax:

routes.MapRoute(
   "MyRoute", "My/Route/Path",
   new {controller = "Home", action = "Index"});

Has anyone run into this? Am I missing something?

EDIT:

I've followed the MVC code down to the point that it hands the route processing off to System.Routing and found something very interesting. The code that MVC eventually runs to lookup the desired URL (condensed, of course) returns an empty string:

Controller.Url.RouteCollection.GetVirtualPath(
          Controller.Url.RequestContext, 
          "MyRoute", new RouteValueDictionary()).VirtualPath;

whereas an extremely similar variant returns the expected string:

Controller.Url.RouteCollection["MyRoute"].GetVirtualPath(
          Controller.Url.RequestContext, 
          new RouteValueDictionary()).VirtualPath;

I can't seem to go any further in the underlying code to see what is actually happening differently here, but thought it might help someone understand what setup I am missing. (I'm not going to yell bug yet, as the fact stands that the UrlHelpers do work when in a real HttpContext)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

只等公子 2024-09-19 17:19:26

我的问题的解决方案已经发布到另一个SO问题。

我之前曾尝试将这个解决方案零碎地融入其中,但效果很差。一旦我完全复制它并根据我的情况进行修改,它就完美地工作了。

这是一个更通用的版本,可以在许多测试中重用(如果放置在基本测试夹具类或类似的东西中)。

用法:

var controller = GetController<MyController>();
controller.MyAction();
//...

方法:

protected T GetController<T>() where T : Controller, new()
{
    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", 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(It.IsAny<string>())).Returns((string p) => p);

    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 T();
    controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
    controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);

    return controller;
}

The solution to my problem was already posted to another SO question.

I had tried to incorporate this solution piece-meal earlier but did so poorly. Once I copied it entirely and modified for my situation, it worked perfectly.

Here is a more generic version that can be reused across many tests (if placed in a base test fixture class or something similar).

Usage:

var controller = GetController<MyController>();
controller.MyAction();
//...

Method:

protected T GetController<T>() where T : Controller, new()
{
    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", 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(It.IsAny<string>())).Returns((string p) => p);

    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 T();
    controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
    controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);

    return controller;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文