Я хотел добавить в свою службу фильтр действий, чтобы обрабатывать добавление данных ссылки в ответное сообщение. Я обнаружил, что мне нужно издеваться над HttpActionExecutedContext, но это сложный класс, чтобы высмеять, как вы имеете дело с тестированием Action Filter?
Как вы можете unit test создать фильтр действий в ASP.NET Web Api?
Ответ 1
Вы можете создать подделку для HttpActionExecutedContext
, как показано ниже:
public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null)
{
HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext();
HttpActionDescriptor descriptor = actionDescriptor ?? new Mock<HttpActionDescriptor>() { CallBase = true }.Object;
return new HttpActionContext(context, descriptor);
}
public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response };
return actionExecutedContext;
}
Я только что скопировал и вставил этот код из исходного кода веб-API ASP.NET: ContextUtil. Вот несколько примеров того, как они протестировали некоторые встроенные фильтры:
ActionFilterAttributeTest
- это тестовый класс для ActionFilterAttribute
, который является абстрактным классом, но вы получите эту идею.
Ответ 2
Просто новый.
private HttpActionContext CreateExecutingContext()
{
return new HttpActionContext { ControllerContext = new HttpControllerContext { Request = new HttpRequestMessage() } };
}
private HttpActionExecutedContext CreateExecutedContextWithStatusCode(HttpStatusCode statusCode)
{
return new HttpActionExecutedContext
{
ActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = new HttpRequestMessage()
}
},
Response = new HttpResponseMessage
{
StatusCode = statusCode,
Content = new StringContent("blah")
}
};
}
Ответ 3
У меня была такая же проблема при попытке протестировать пользовательский необработанный фильтр исключений, который я создал.
Это сделало трюк. Множество новинок и очень длинная строка кода.
var httpActionExecutedContext = new HttpActionExecutedContext(
new HttpActionContext(
new HttpControllerContext(
new HttpConfiguration(),
Substitute.For<IHttpRouteData>(),
new HttpRequestMessage()),
Substitute.For<HttpActionDescriptor>()),
null);
NSubstiute был использован, но любая издевательская структура по вашему выбору, которая обрабатывает абстрактные базовые классы, будет прекрасной.
Надеюсь, что это поможет
Ответ 4
Я тоже ударился головой о кирпичную стену. Я попробовал contextUtil, но продолжал получать исключение с нулевой ссылкой. Я узнал, как вызвать actionFilter в этом сообщении Нотабене Действие actionFilter не вызывается при использовании экземпляра Mock фильтра, я должен был использовать реальный объект. НТН
В частности:
var httpActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = requestMessage
}
};
//call filter
var filter = new FooFilter();
filter.OnActionExecuting(httpActionContext);
Ответ 5
Вот рабочий пример с 2018 года (.NET Framework 4.5.1). Он использует ExceptionFilterAttribute, но он должен быть аналогичным для других FilterAttributes.
[Test]
public void MyTest()
{
var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.google.com"));
var response = new HttpResponseMessage();
// This next line is necessary to avoid the following error
// if you call 'context.Request.CreateResponse(...)' inside the filter:
// System.InvalidOperationException: The request does not have an associated configuration object or the provided configuration was null.
// Discovered from https://stackoverflow.com/a/44447355/3312114
request.SetConfiguration(new HttpConfiguration());
var context = ContextUtil.GetActionExecutedContext(request, response);
_myFilter.OnException(context); // Execute your methods
Assert.AreEqual(HttpStatusCode.InternalServerError, context.Response.StatusCode); // Make your assertions
}
Затем просто скопируйте класс ContextUtil в ваш тестовый проект. Комментарий @thomasb к ответу @tugberk предполагает, что последний код находится на Codeplex. В то время как этот комментарий был в 2014 году, так что может быть даже более поздний код, код 2014 года работал для меня (в январе 2018 года), в то время как исходный связанный код не работал. Я скопировал более позднюю версию ниже для удобства. Просто поместите это в новый файл.
internal static class ContextUtil
{
public static HttpControllerContext CreateControllerContext(HttpConfiguration configuration = null, IHttpController instance = null, IHttpRouteData routeData = null, HttpRequestMessage request = null)
{
HttpConfiguration config = configuration ?? new HttpConfiguration();
IHttpRouteData route = routeData ?? new HttpRouteData(new HttpRoute());
HttpRequestMessage req = request ?? new HttpRequestMessage();
req.SetConfiguration(config);
req.SetRouteData(route);
HttpControllerContext context = new HttpControllerContext(config, route, req);
if (instance != null)
{
context.Controller = instance;
}
context.ControllerDescriptor = CreateControllerDescriptor(config);
return context;
}
public static HttpActionContext CreateActionContext(HttpControllerContext controllerContext = null, HttpActionDescriptor actionDescriptor = null)
{
HttpControllerContext context = controllerContext ?? ContextUtil.CreateControllerContext();
HttpActionDescriptor descriptor = actionDescriptor ?? CreateActionDescriptor();
descriptor.ControllerDescriptor = context.ControllerDescriptor;
return new HttpActionContext(context, descriptor);
}
public static HttpActionContext GetHttpActionContext(HttpRequestMessage request)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
return actionContext;
}
public static HttpActionExecutedContext GetActionExecutedContext(HttpRequestMessage request, HttpResponseMessage response)
{
HttpActionContext actionContext = CreateActionContext();
actionContext.ControllerContext.Request = request;
HttpActionExecutedContext actionExecutedContext = new HttpActionExecutedContext(actionContext, null) { Response = response };
return actionExecutedContext;
}
public static HttpControllerDescriptor CreateControllerDescriptor(HttpConfiguration config = null)
{
if (config == null)
{
config = new HttpConfiguration();
}
return new HttpControllerDescriptor() { Configuration = config, ControllerName = "FooController" };
}
public static HttpActionDescriptor CreateActionDescriptor()
{
var mock = new Mock<HttpActionDescriptor>() { CallBase = true };
mock.SetupGet(d => d.ActionName).Returns("Bar");
return mock.Object;
}
}
Ответ 6
Ссылка fooobar.com/info/1678828/...
Вы можете создать HTTPActionContext самостоятельно с помощью следующего:
_ctx = new HttpActionContext
{
ControllerContext = new HttpControllerContext()
{
Request = new HttpRequestMessage()
}
};
_ctx.Request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();
Хитрость без настройки записи Request.Properties, она покажет ошибку:
У запроса нет связанного объекта конфигурации, или предоставленная конфигурация была нулевой.
Это может быть упущением со стороны дизайнеров, так как вы можете установить HTTPConfiguration в конструкторе HTTPActionContext!