.net-如何在ASP.NET Web Api中对动作筛选器进行单元测试?

我一直想在我的服务中添加一个动作过滤器,以处理将链接数据添加到响应消息中的过程。 我发现我需要模拟HttpActionExecutedContext,但这是一个很难模拟的类,您如何处理Action Filter测试?

6个解决方案
58 votes

您可以如下创建ActionFilterAttributeTest的伪造品:

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;
}

我只是从ASP.NET Web API源代码ContextUtil类复制并粘贴了该代码。 下面是一些有关他们如何测试内置过滤器的示例:

  • 授权属性测试

  • ActionFilterAttribute测试

ActionFilterAttributeTestActionFilterAttribute的测试类,它是抽象类,但您会明白的。

tugberk answered 2020-01-13T20:12:15Z
25 votes

只是新的。

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")
        }
    };
}
Sam Shiles answered 2020-01-13T20:12:35Z
11 votes

尝试测试构建的自定义未处理异常过滤器时,我遇到了同样的问题。

这成功了。 大量的更新和很长的代码行。

var httpActionExecutedContext = new HttpActionExecutedContext(
    new HttpActionContext(
        new HttpControllerContext(
            new HttpConfiguration(),
            Substitute.For<IHttpRouteData>(),
            new HttpRequestMessage()),
    Substitute.For<HttpActionDescriptor>()),
    null);

使用了NSubstiute,但是您选择的任何处理抽象基类的模拟框架都可以。

希望这可以帮助

will webster answered 2020-01-13T20:13:08Z
6 votes

我也一直在这上面撞墙。 我尝试了contextUtil,但一直收到null引用异常。 我在这篇文章中发现了如何调用actionFilterN.B. 使用过滤器的Mock实例时,没有调用actionFilter,我必须使用真实对象。高温超导

特别:

var httpActionContext = new HttpActionContext
{
    ControllerContext = new HttpControllerContext
    {
        Request = requestMessage
    }
};

//call filter
var filter = new FooFilter();
filter.OnActionExecuting(httpActionContext);
outofcoolnames answered 2020-01-13T20:13:33Z
2 votes

这是2018年的工作示例(.NET Framework 4.5.1)。 它使用ExceptionFilterAttribute,但对于其他FilterAttribute应该与之相似。

[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年1月),而原始的链接代码却没有。 为了方便起见,我在下面复制了更高版本。 只需将其放入新文件即可。

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;
    }
}
Seafish answered 2020-01-13T20:13:58Z
2 votes

引用[https://stackoverflow.com/a/44447349/5547177]

您可以使用以下命令自己创建一个HTTPActionContext:

 _ctx = new HttpActionContext
        {
            ControllerContext = new HttpControllerContext()
            {
                Request = new HttpRequestMessage()

            }
        };
        _ctx.Request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();

诀窍是没有Request.Properties条目设置,它将显示以下错误:

该请求没有关联的配置对象,或者   提供的配置为空。

这可能是设计人员的疏忽,因为您可以在HTTPActionContext构造函数中设置HTTPConfiguration!

3DPrintScanner answered 2020-01-13T20:14:36Z
translate from https://stackoverflow.com:/questions/11848137/how-can-you-unit-test-an-action-filter-in-asp-net-web-api