I am trying to do some unit testing of my WebApi route configuration. I want to test that the route "/api/super"
maps to the Get()
method of my SuperController
. I've setup the below test and am having a few issues.
public void GetTest() { var url = "~/api/super"; var routeCollection = new HttpRouteCollection(); routeCollection.MapHttpRoute("DefaultApi", "api/{controller}/"); var httpConfig = new HttpConfiguration(routeCollection); var request = new HttpRequestMessage(HttpMethod.Get, url); // exception when url = "/api/super" // can get around w/ setting url = "http://localhost/api/super" var routeData = httpConfig.Routes.GetRouteData(request); request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData; var controllerSelector = new DefaultHttpControllerSelector(httpConfig); var controlleDescriptor = controllerSelector.SelectController(request); var controllerContext = new HttpControllerContext(httpConfig, routeData, request); controllerContext.ControllerDescriptor = controlleDescriptor; var selector = new ApiControllerActionSelector(); var actionDescriptor = selector.SelectAction(controllerContext); Assert.AreEqual(typeof(SuperController), controlleDescriptor.ControllerType); Assert.IsTrue(actionDescriptor.ActionName == "Get"); }
My first issue is that if I don't specify a fully qualified URL httpConfig.Routes.GetRouteData(request);
throws a InvalidOperationException
exception with a message of "This operation is not supported for a relative URI."
I'm obviously missing something with my stubbed configuration. I would prefer to use a relative URI as it does not seem reasonable to use a fully qualified URI for route testing.
My second issue with my configuration above is I am not testing my routes as configured in my RouteConfig but am instead using:
var routeCollection = new HttpRouteCollection(); routeCollection.MapHttpRoute("DefaultApi", "api/{controller}/");
How do I make use of the assigned RouteTable.Routes
as configured in a typical Global.asax:
public class MvcApplication : HttpApplication { protected void Application_Start() { // other startup stuff RouteConfig.RegisterRoutes(RouteTable.Routes); } } public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { // route configuration } }
Further what I have stubbed out above may not be the best test configuration. If there is a more streamlined approach I am all ears.
The default route template for Web API is "api/{controller}/{id}". In this template, "api" is a literal path segment, and {controller} and {id} are placeholder variables. When the Web API framework receives an HTTP request, it tries to match the URI against one of the route templates in the routing table.
What is Convention Based Routing in ASP.NET MVC? Routing is a mechanism which is used to handle the incoming requests coming from browsers and it represent the particular action rather than any static or physical files.
I was recently testing my Web API routes, and here is how I did that.
public static class WebApi { public static RouteInfo RouteRequest(HttpConfiguration config, HttpRequestMessage request) { // create context var controllerContext = new HttpControllerContext(config, Substitute.For<IHttpRouteData>(), 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); return new RouteInfo { Controller = controllerDescriptor.ControllerType, Action = actionMapping.ActionName }; } 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); } } } public class RouteInfo { public Type Controller { get; set; } public string Action { get; set; } }
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
[Test] public void GET_api_products_by_id_Should_route_to_ProductsController_Get_method() { // setups var request = new HttpRequestMessage(HttpMethod.Get, "http://myshop.com/api/products/1"); var config = new HttpConfiguration(); // act WebApiConfig.Register(config); var route = WebApi.RouteRequest(config, request); // asserts route.Controller.Should().Be<ProductsController>(); route.Action.Should().Be("Get"); } [Test] public void GET_api_products_Should_route_to_ProductsController_GetAll_method() { // setups var request = new HttpRequestMessage(HttpMethod.Get, "http://myshop.com/api/products"); var config = new HttpConfiguration(); // act WebApiConfig.Register(config); var route = WebApi.RouteRequest(config, request); // asserts route.Controller.Should().Be<ProductsController>(); route.Action.Should().Be("GetAll"); } ....
Some notes below:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With