测试请求的URL是否在Route表中

我想测试一个URL是否是Global.asax定义的路由的一部分。 这就是我所拥有的:

 var TheRequest = HttpContext.Current.Request.Url.AbsolutePath.ToString(); var TheRoutes = System.Web.Routing.RouteTable.Routes; foreach (var TheRoute in TheRoutes) { if (TheRequest == TheRoute.Url) //problem here { RequestIsInRoutes = true; } } 

问题是我无法从路由中提取URL。 我需要改变什么?

问题是我无法从路由中提取URL。

我不同意。 问题是您希望将URL拉出路由表并在外部进行比较。 此外,目前还不清楚你希望通过这样做获得什么。

路由将传入请求与业务逻辑进行比较,以确定它是否匹配。 这是路线的目的 。 将匹配逻辑移到路由之外不是有效测试,因为您没有测试路由实现的业务逻辑。

更不用说,假设路由只能匹配URL而不是请求中的任何其他内容(例如表单发布值或cookie),这有点推测。 虽然内置路由function仅匹配URL,但没有什么能阻止您制作符合其他条件的约束或自定义路由。

因此,简而言之,您需要为路由中的业务逻辑编写unit testing。 在路由配置之外发生的任何逻辑都应单独进行unit testing。

Brad Wilson有一篇很棒的文章 (虽然有点陈旧),演示了如何对你的路线进行unit testing。 我已经更新了代码以使用MVC 5 – 这是一个使用下面代码的工作演示 。

IncomingRouteTests.cs

 using Microsoft.VisualStudio.TestTools.UnitTesting; using MvcRouteTesting; using System.Web.Mvc; using System.Web.Routing; [TestClass] public class IncomingRouteTests { [TestMethod] public void RouteWithControllerNoActionNoId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("Index", routeData.Values["action"]); Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]); } [TestMethod] public void RouteWithControllerWithActionNoId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1/action2"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("action2", routeData.Values["action"]); Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]); } [TestMethod] public void RouteWithControllerWithActionWithId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1/action2/id3"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("action2", routeData.Values["action"]); Assert.AreEqual("id3", routeData.Values["id"]); } [TestMethod] public void RouteWithTooManySegments() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/a/b/c/d"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNull(routeData); } [TestMethod] public void RouteForEmbeddedResource() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/foo.axd/bar/baz/biff"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.IsInstanceOfType(routeData.RouteHandler, typeof(StopRoutingHandler)); } } 

OutgoingRouteTests.cs

 using Microsoft.VisualStudio.TestTools.UnitTesting; using MvcRouteTesting; using System.Web; using System.Web.Mvc; using System.Web.Routing; [TestClass] public class OutgoingRouteTests { [TestMethod] public void ActionWithAmbientControllerSpecificAction() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action"); Assert.AreEqual("/defaultcontroller/action", url); } [TestMethod] public void ActionWithSpecificControllerAndAction() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action", "controller"); Assert.AreEqual("/controller/action", url); } [TestMethod] public void ActionWithSpecificControllerActionAndId() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action", "controller", new { id = 42 }); Assert.AreEqual("/controller/action/42", url); } [TestMethod] public void RouteUrlWithAmbientValues() { UrlHelper helper = GetUrlHelper(); string url = helper.RouteUrl(new { }); Assert.AreEqual("/defaultcontroller/defaultaction", url); } [TestMethod] public void RouteUrlWithAmbientValuesInSubApplication() { UrlHelper helper = GetUrlHelper(appPath: "/subapp"); string url = helper.RouteUrl(new { }); Assert.AreEqual("/subapp/defaultcontroller/defaultaction", url); } [TestMethod] public void RouteUrlWithNewValuesOverridesAmbientValues() { UrlHelper helper = GetUrlHelper(); string url = helper.RouteUrl(new { controller = "controller", action = "action" }); Assert.AreEqual("/controller/action", url); } static UrlHelper GetUrlHelper(string appPath = "/", RouteCollection routes = null) { if (routes == null) { routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); } HttpContextBase httpContext = new StubHttpContextForRouting(appPath); RouteData routeData = new RouteData(); routeData.Values.Add("controller", "defaultcontroller"); routeData.Values.Add("action", "defaultaction"); RequestContext requestContext = new RequestContext(httpContext, routeData); UrlHelper helper = new UrlHelper(requestContext, routes); return helper; } } 

Stubs.cs

 using System; using System.Collections.Specialized; using System.Web; public class StubHttpContextForRouting : HttpContextBase { StubHttpRequestForRouting _request; StubHttpResponseForRouting _response; public StubHttpContextForRouting(string appPath = "/", string requestUrl = "~/") { _request = new StubHttpRequestForRouting(appPath, requestUrl); _response = new StubHttpResponseForRouting(); } public override HttpRequestBase Request { get { return _request; } } public override HttpResponseBase Response { get { return _response; } } public override object GetService(Type serviceType) { return null; } } public class StubHttpRequestForRouting : HttpRequestBase { string _appPath; string _requestUrl; public StubHttpRequestForRouting(string appPath, string requestUrl) { _appPath = appPath; _requestUrl = requestUrl; } public override string ApplicationPath { get { return _appPath; } } public override string AppRelativeCurrentExecutionFilePath { get { return _requestUrl; } } public override string PathInfo { get { return ""; } } public override NameValueCollection ServerVariables { get { return new NameValueCollection(); } } } public class StubHttpResponseForRouting : HttpResponseBase { public override string ApplyAppPathModifier(string virtualPath) { return virtualPath; } } 

有了这个,回到你原来的问题。

如何确定URL是否在路由表中?

这个问题有点推定。 正如其他人所指出的,路由表不包含URL,它包含业务逻辑。 一个更正确的方式来表达问题将是:

如何确定传入的URL是否与路由表中的任何路由匹配?

那你就在路上。

为此,您需要在路径集合中执行GetRouteData业务逻辑。 这将在每个路由上执行GetRouteData方法,直到它们中的第一个返回RouteData对象而不是null 。 如果它们都没有返回RouteData对象(即,所有路由都返回null ),则表示没有路由匹配该请求。

换句话说, GetRouteDatanull结果表示没有任何路由与请求匹配。 RouteData对象指示其中一个路由匹配,并提供必要的路由数据(控制器,操作等)以使MVC与操作方法匹配。

因此,要简单地检查URL是否与路由匹配,您只需确定操作的结果是否为null

 [TestMethod] public void EnsureHomeAboutMatches() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/home/about"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); } 

另请注意, 生成路由是与匹配传入路由的单独任务。 您可以从路由生成传出URL,但它使用与匹配传入路由完全不同的业务逻辑集。 这个传出的URL逻辑可以(并且应该)与传入的URL逻辑分开进行unit testing,如上所示。

我不知道这是否是您想要的路线,如果是这种情况,您可以从当前请求中获取:

 var route = HttpContext.Current.Request.RequestContext.RouteData.Route; 

您可以尝试根据路由表检查当前上下文

 var contextBase = HttpContext.Current.Request.RequestContext.HttpContext; var data = RouteTable.Routes.GetRouteData(contextBase); if (data != null) { //Route exists } 

使用以上作为创建服务的基础

 public interface IRouteInspector { bool RequestIsInRoutes(); } public interface IHttpContextAccessor { HttpContextBase HttpContext { get; } } public interface IRouteTable { RouteCollection Routes { get; } } public class RouteInspector : IRouteInspector { private IRouteTable routeTable; private IHttpContextAccessor contextBase; public RouteInspector(IRouteTable routeTable, IHttpContextAccessor contextBase) { this.routeTable = routeTable; this.contextBase = contextBase; } public bool RequestIsInRoutes() { if (routeTable.Routes.GetRouteData(contextBase.HttpContext) != null) { //Route exists return true; } return false; } } 

这里是测试类,显示它是如何使用的。

 [TestClass] public class RouteTableUnitTests : ControllerUnitTests { [TestMethod] public void Should_Get_Request_From_Route_Table() { //Arrange var contextBase = new Mock(); contextBase.Setup(m => m.HttpContext) .Returns(HttpContext.Current.Request.RequestContext.HttpContext); var routeTable = new Mock(); routeTable.Setup(m => m.Routes).Returns(RouteTable.Routes); var sut = new RouteInspector(routeTable.Object, contextBase.Object); //Act var actual = sut.RequestIsInRoutes(); //Assert Assert.IsTrue(actual); } } 

有重构和改进的空间,但这是一个开始。

这就是我最终做的事情:

 string TheRequest = HttpContext.Current.Request.Url.AbsolutePath.ToString(); foreach (Route r in System.Web.Routing.RouteTable.Routes) { if (("/" + r.Url) == TheRequest) { //the request is in the routes } } 

这是hacky但它​​有3行。