Тестирование ASP.NET Web API POST и GET-маршрутов без параметров с помощью WebApiContrib.Testing
Я пытаюсь настроить некоторые тесты маршрутов с помощью библиотеки WebApiContrib.Testing. Мои тесты get (как это) работают нормально...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsASingleItemGetRouteWithAHashString()
{
"~/auth/sjkfhiuehfkshjksdfh".ShouldMapTo<AuthController>(c => c.Get("sjkfhiuehfkshjksdfh"));
}
Я довольно потерян на пост-тесте - в настоящее время у меня есть следующее, что не с NotImplementedException...
[Test]
[Category("Auth Api Tests")]
public void TheAuthControllerAcceptsAPost()
{
"~/auth".ShouldMapTo<AuthController>(c => c.Post(new AuthenticationCredentialsModel()), "POST");
}
Вот настройки и разбор для полноты...
[SetUp]
public void SetUpTest()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
[TearDown]
public void TearDownTest()
{
RouteTable.Routes.Clear();
GlobalConfiguration.Configuration.Routes.Clear();
}
Маршрут, который я пытаюсь проверить, является маршрутом POST по умолчанию, который сопоставляется с вызовом этого метода...
[AllowAnonymous]
public HttpResponseMessage Post([FromBody] AuthenticationCredentialsModel model)
{ *** Some code here that doesn't really matter *** }
Я также получаю ошибку в этом тесте, который проверяет стандартный маршрут GET без параметров, возвращает все элементы...
[Test]
[Category("VersionInfo Api Tests")]
public void TheVersionInfoControllerAcceptsAMultipleItemGetRouteForAllItems()
{
"~/versioninfo".ShouldMapTo<VersionInfoController>(c => c.Get());
}
Который тестирует этот метод...
public HttpResponseMessage Get()
{ *** Some code here that doesn't really matter *** }
Эта библиотека была рекомендована несколькими статьями, которые я прочитал, но сейчас я не уверен, что делаю что-то не так или она просто ограничена, и мне лучше откатить свою собственную.
2 ответа
В конце концов я исправил это, написав свое собственное, прочитав пост whyleee по другому вопросу здесь - Тестирование конфигурации маршрута в ASP.NET WebApi (WebApiContrib.Testing, похоже, не работает для меня)
Я объединил его пост с некоторыми элементами, которые мне нравились синтаксически из библиотеки WebApiContrib.Testing, чтобы сгенерировать следующий вспомогательный класс.
Это позволяет мне писать действительно легкие тесты, как это...
[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; }
}
}
Кажется, здесь есть некоторые неприятные проблемы - после отладки через библиотеку WebApiContrib.Testing я обнаружил следующее...
В коде сопоставления маршрутов, который используется для собственных тестов библиотек, сопоставления маршрутов выглядят следующим образом...
// GET /api/{resource}
routes.MapHttpRoute(
name: "Web API Get All",
routeTemplate: "api/{controller}",
defaults: new {action = "Get"},
constraints: new {httpMethod = new HttpMethodConstraint("GET")}
);
Мое сопоставление маршрутов выглядит следующим образом для одного и того же маршрута...
config.Routes.MapHttpRoute("DefaultApiGet", "{controller}",
new { action = "Get" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
Обратите внимание на другой синтаксис для построения ограничений. Мой код отображения маршрута не позволяет использовать строки. Может быть, это что-то изменилось в версии WebAPI, так как библиотека была написана, я не уверен, но кажется, что любой маршрут, который я пытаюсь проверить с HttpMethodConstraint
не удастся.
Я продолжаю исследовать это; Теперь у меня есть "почему", но пока нет "как исправить".
ОБНОВЛЕНИЕ: Маршрутизация WebAPI принимает две формы ограничения, одна из которых является просто строкой (подробнее здесь - http://forums.asp.net/p/1777747/4904556.aspx/1?Re+System+Web+Http+Routing+IHttpRouteConstraint+vs+System+Web+Routing+IRouteConstraint). Я попытался изменить таблицу маршрутизации, чтобы понять, будет ли это иметь какое-то значение, но это не помогло - просто чтобы вы знали!