Szczególnie w przypadku WebAPI, routing może być dość skomplikowany. W Nancy bardzo łatwo przetestować mapowanie między URL a zaimplementowaną logiką. W WebAPI moim zdaniem jest to dużo mniej wygodnie, ale wciąż powinniśmy zadbać o to, aby mieć zautomatyzowane testy dla routing’u.
Załóżmy, że mamy kontroler z niestandardowym routingiem:
public class SampleController : ApiController { [Route("site/{siteId}/person/{personid}/profile/words/{searchText}")] public int[] GetData(int siteId, int personId, string searchText) { return Enumerable.Range(1, 10).ToArray(); } }
Przykładowy, dopasowany URL to “http://localhost:35447/site/1/person/3/profile/words/pi”.
Możemy mieć przetestowaną całą logikę, zwracane kody HTTP, ale i tak bez testów routingu ryzykujemy bardzo dużo.
Zacznijmy od testu kontrolera. Chcemy upewnić się, że wywołanie powyższego linka, spowoduje zmapowanie do SampleController:
[TestFixture] public class RoutingTests { const string Url = "http://www.test.com/site/1/person/3/profile/words/ppp"; [SetUp] public void Setup() { } [Test] public void VerifyController() { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.EnsureInitialized(); IHttpControllerSelector controllerSelector = new DefaultHttpControllerSelector(config); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Url); var routeData = config.Routes.GetRouteData(request); request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData; HttpControllerDescriptor descriptor = controllerSelector.SelectController(request); Assert.That(descriptor.ControllerType, Is.EqualTo(typeof (SampleController))); } }
Najpierw tworzymy instancję HttpConfiguration oraz wywołujemy MapHttpAttributeRoutes, ponieważ w powyższym przykładzie, użyliśmy atrybutów do konfiguracji routingu.
Następnie musimy zainicjalizować IHttpControllerSelector – klasę odpowiedzialną za wybranie kontrolera na podstawie URL.
W teście, korzystamy z HttpRequestMessage. Niezbędne jest zatem przekazanie danych routingu za pomocą:
var routeData = config.Routes.GetRouteData(request); request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
Przejdźmy teraz do rozszerzenia powyższego testu, tak aby zawierał on również akcję:
[Test] public void VeryfiControllerAndAction() { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.EnsureInitialized(); IHttpControllerSelector controllerSelector = new DefaultHttpControllerSelector(config); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Url); var routeData = config.Routes.GetRouteData(request); request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData; HttpControllerDescriptor controllerDescriptor = controllerSelector.SelectController(request); HttpControllerContext controllerContext = new HttpControllerContext(config, routeData, request); controllerContext.ControllerDescriptor = controllerDescriptor; IHttpActionSelector actionSelector = new ApiControllerActionSelector(); HttpActionDescriptor actionDescriptor = actionSelector.SelectAction(controllerContext); Assert.That(actionDescriptor.ActionName, Is.EqualTo("GetData")); Assert.That(controllerDescriptor.ControllerType, Is.EqualTo(typeof(SampleController))); }
Widzimy, że sprawa trochę się skomplikowała, ale zasada jest analogiczna – potrzebujemy dodatkowo dostępu do IHttpActionSelector.
Powyższy kod jest zdecydowanie zły i brzydki. W następnym w poście napiszemy wrapper, który umożliwi nam korzystanie z tego w produkcyjnym kodzie. Ponadto, to nie jest jedyny sposób testowania rountingu w ASP.NET i zajmiemy się również innymi podejściami.