Unit testing 在ASP.NET WebApi中测试路由配置
我正在尝试对我的路由配置进行一些单元测试。我想测试路由Unit testing 在ASP.NET WebApi中测试路由配置,unit-testing,routes,asp.net-web-api,Unit Testing,Routes,Asp.net Web Api,我正在尝试对我的路由配置进行一些单元测试。我想测试路由“/api/super”是否映射到我的超级控制器的Get()方法。我已经设置了下面的测试,并且有一些问题 public void GetTest() { var url=“~/api/super”; var routeCollection=新的HttpRouteCollection(); MapHttpRoute(“DefaultApi”,“api/{controller}/”; var httpConfig=新的HttpConfigurat
“/api/super”
是否映射到我的超级控制器的Get()
方法。我已经设置了下面的测试,并且有一些问题
public void GetTest()
{
var url=“~/api/super”;
var routeCollection=新的HttpRouteCollection();
MapHttpRoute(“DefaultApi”,“api/{controller}/”;
var httpConfig=新的HttpConfiguration(routeCollection);
var request=newhttprequestmessage(HttpMethod.Get,url);
//url=“/api/super”时出现异常
//可以通过设置url=”四处走动http://localhost/api/super"
var routeData=httpConfig.Routes.GetRouteData(请求);
request.Properties[HttpPropertyKeys.HttpRouteDataKey]=路由数据;
var controllerSelector=新的默认HttpControllerSelector(httpConfig);
var controlleredescriptor=controllerSelector.SelectController(请求);
var控制器上下文=
新的HttpControllerContext(httpConfig、RoutedData、request);
controllerContext.ControllerDescriptor=ControllerDescriptor;
var选择器=新的ApiControllerActionSelector();
var actionDescriptor=selector.SelectAction(controllerContext);
Assert.AreEqual(类型化(超级控制器),
controlleDescriptor.ControllerType);
Assert.IsTrue(actionDescriptor.ActionName==“Get”);
}
我的第一个问题是,如果我没有指定一个完全限定的URLhttpConfig.Routes.GetRouteData(请求)
抛出一个invalidoOperationException
异常,并显示一条消息“相对URI不支持此操作”
很明显,我的存根配置遗漏了一些东西。我更喜欢使用相对URI,因为在路由测试中使用完全限定的URI似乎不合理
我的上述配置的第二个问题是,我没有按照RouteConfig中的配置测试路由,而是使用:
var-routeCollection=new-HttpRouteCollection();
MapHttpRoute(“DefaultApi”,“api/{controller}/”;
如何使用在典型的Global.asax中配置的分配的RouteTable.Routes
:
public类mvcapapplication:HttpApplication
{
受保护的无效应用程序\u Start()
{
//其他初创公司的东西
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
公共类路由图
{
公共静态无效注册表项(路由收集路由)
{
//路由配置
}
}
此外,我在上面指出的可能不是最好的测试配置。如果有更精简的方法,我洗耳恭听。要从路由集合获取路由数据,在这种情况下,您确实需要提供完整的URI(只需使用“http://localhost/api/super").
要从RouteTable.routes测试路由,您可以执行以下操作:
var httpConfig = GlobalConfiguration.Configuration;
httpConfig.Routes.MapHttpRoute("DefaultApi", "api/{controller}/");
实际上,GlobalConfiguration将RouteTable.Routes修改为httpConfig.Routes。因此,当您将路由添加到httpConfig.Routes时,它实际上会添加到RouteTable.Routes。但要使其工作,您需要托管在ASP.NET中,以便填充诸如HostingEnvironment.ApplicationVirtualPath之类的环境设置。我最近正在测试我的Web API路由,下面是我如何做到的
首先,我创建了一个助手,将所有Web API路由逻辑移动到那里:
公共静态类WebApi
{
公共静态路由信息路由请求(HttpConfiguration配置、HttpRequestMessage请求)
{
//创建上下文
var controllerContext=new-HttpControllerContext(config,Substitute.For(),request);
//获取路线数据
var routeData=config.Routes.GetRouteData(请求);
移除可选路由参数(路由数据值);
request.Properties[HttpPropertyKeys.HttpRouteDataKey]=路由数据;
controllerContext.RoutedData=RoutedData;
//获取控制器类型
var controllerDescriptor=新的默认HttpControllerSelector(配置)。SelectController(请求);
controllerContext.ControllerDescriptor=ControllerDescriptor;
//获取操作名称
var actionMapping=new ApiControllerActionSelector().SelectAction(controllerContext);
返回新路由信息
{
控制器=controllerDescriptor.ControllerType,
Action=actionMapping.ActionName
};
}
私有静态void RemoveOptionalRoutingParameters(IDictionary RouteValue)
{
var optionalParams=路由值
.Where(x=>x.Value==RouteParameter.Optional)
.选择(x=>x.Key)
.ToList();
foreach(可选参数中的var键)
{
路由值。移除(键);
}
}
}
公共类路由信息
{
公共类型控制器{get;set;}
公共字符串操作{get;set;}
}
假设我有一个单独的类来注册Web API路由(默认情况下,它是在Visual Studio ASP.NET MVC 4 Web应用程序项目的App_Start文件夹中创建的):
公共静态类WebApiConfig
{
公共静态无效寄存器(HttpConfiguration配置)
{
config.Routes.MapHttpRoute(
名称:“DefaultApi”,
routeTemplate:“api/{controller}/{id}”,
默认值:新建{id=RouteParameter.Optional}
);
}
}
我可以轻松测试我的路线:
[测试]
public void获取\u api\u产品\u按\u id获取\u应\u路由\u到\u产品控制器\u获取\u方法()
{
//设置
var request=newhttprequestmessage(HttpMethod.Get)http://myshop.com/api/products/1");
var config=新的HttpConfiguration();
//表演
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"http://api.siansplan.com/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>("Get", "hash");
}
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"http://api.siansplan.com/auth".ShouldMapTo<AuthController>("Post", HttpMethod.Post);
}
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Hosting;
using System.Web.Http.Routing;
namespace SiansPlan.Api.Tests.Helpers
{
public static class RoutingTestHelper
{
/// <summary>
/// Routes the request.
/// </summary>
/// <param name="config">The config.</param>
/// <param name="request">The request.</param>
/// <returns>Inbformation about the route.</returns>
public static RouteInfo RouteRequest(HttpConfiguration config, HttpRequestMessage request)
{
// create context
var controllerContext = new HttpControllerContext(config, new Mock<IHttpRouteData>().Object, request);
// get route data
var routeData = config.Routes.GetRouteData(request);
RemoveOptionalRoutingParameters(routeData.Values);
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
controllerContext.RouteData = routeData;
// get controller type
var controllerDescriptor = new DefaultHttpControllerSelector(config).SelectController(request);
controllerContext.ControllerDescriptor = controllerDescriptor;
// get action name
var actionMapping = new ApiControllerActionSelector().SelectAction(controllerContext);
var info = new RouteInfo(controllerDescriptor.ControllerType, actionMapping.ActionName);
foreach (var param in actionMapping.GetParameters())
{
info.Parameters.Add(param.ParameterName);
}
return info;
}
#region | Extensions |
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, params string[] parameterNames)
{
return ShouldMapTo<TController>(fullDummyUrl, action, HttpMethod.Get, parameterNames);
}
/// <summary>
/// Determines that a URL maps to a specified controller.
/// </summary>
/// <typeparam name="TController">The type of the controller.</typeparam>
/// <param name="fullDummyUrl">The full dummy URL.</param>
/// <param name="action">The action.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <param name="parameterNames">The parameter names.</param>
/// <returns></returns>
/// <exception cref="System.Exception"></exception>
public static bool ShouldMapTo<TController>(this string fullDummyUrl, string action, HttpMethod httpMethod, params string[] parameterNames)
{
var request = new HttpRequestMessage(httpMethod, fullDummyUrl);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
var route = RouteRequest(config, request);
var controllerName = typeof(TController).Name;
if (route.Controller.Name != controllerName)
throw new Exception(String.Format("The specified route '{0}' does not match the expected controller '{1}'", fullDummyUrl, controllerName));
if (route.Action.ToLowerInvariant() != action.ToLowerInvariant())
throw new Exception(String.Format("The specified route '{0}' does not match the expected action '{1}'", fullDummyUrl, action));
if (parameterNames.Any())
{
if (route.Parameters.Count != parameterNames.Count())
throw new Exception(
String.Format(
"The specified route '{0}' does not have the expected number of parameters - expected '{1}' but was '{2}'",
fullDummyUrl, parameterNames.Count(), route.Parameters.Count));
foreach (var param in parameterNames)
{
if (!route.Parameters.Contains(param))
throw new Exception(
String.Format("The specified route '{0}' does not contain the expected parameter '{1}'",
fullDummyUrl, param));
}
}
return true;
}
#endregion
#region | Private Methods |
/// <summary>
/// Removes the optional routing parameters.
/// </summary>
/// <param name="routeValues">The route values.</param>
private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValues)
{
var optionalParams = routeValues
.Where(x => x.Value == RouteParameter.Optional)
.Select(x => x.Key)
.ToList();
foreach (var key in optionalParams)
{
routeValues.Remove(key);
}
}
#endregion
}
/// <summary>
/// Route information
/// </summary>
public class RouteInfo
{
#region | Construction |
/// <summary>
/// Initializes a new instance of the <see cref="RouteInfo"/> class.
/// </summary>
/// <param name="controller">The controller.</param>
/// <param name="action">The action.</param>
public RouteInfo(Type controller, string action)
{
Controller = controller;
Action = action;
Parameters = new List<string>();
}
#endregion
public Type Controller { get; private set; }
public string Action { get; private set; }
public List<string> Parameters { get; private set; }
}
}
[TestMethod]
public void RouteToGetUser()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:4567/api/users/me");
var config = new HttpConfiguration();
WebApiConfig.Register(config);
config.EnsureInitialized();
var result = config.Routes.GetRouteData(request);
Assert.AreEqual("api/{controller}/{id}", result.Route.RouteTemplate);
}