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

时间:2010-08-04 19:35:55

标签: asp.net-mvc-2 asp.net-mvc-routing

我遇到的问题是UrlHelper的RouteUrl方法在我的测试中运行时只返回一个空字符串,尽管在真正的HttpContext中执行时功能正常。然而,它正在寻找路线 - 如果我尝试解决尚未定义的路线名称,我会正确地获得异常。

我使用code provided by Scott Hanselman/Kzu嘲笑了HttpContext和朋友,并将code needed to bootstrap the Application's Routes添加到了模拟实例中

为了减少我的情况中的变量数量,我写了一个简单的测试:

[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中工作时)

1 个答案:

答案 0 :(得分:1)

我的问题的解决方案已发布到another SO question

我曾尝试过这种解决方案早餐,但做得很差。一旦我完全复制并根据我的情况进行了修改,它就完美无缺。

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

用法:

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;
}