Как вы можете unit test создать фильтр действий в ASP.NET Web Api?

Я хотел добавить в свою службу фильтр действий, чтобы обрабатывать добавление данных ссылки в ответное сообщение. Я обнаружил, что мне нужно издеваться над HttpActionExecutedContext, но это сложный класс, чтобы высмеять, как вы имеете дело с тестированием Action Filter?

Ответ 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!