c# WebApi how to unit test if a controller action

2019-08-16 16:44发布

We have a custom AuthorizationFilterAttribute for decorating WebApi controller actions that takes an enum parameter to define the level of access.

public enum AuthLevel
{
    Any = 1,
    Client = 2,
    Server = 4
}

[AttributeUsage(AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizationFilterAttribute
{
    private readonly AuthLevel _authLevel;

    public CustomAuthorizeAttribute(AuthLevel authLevel)
    {
        _authLevel = authLevel;
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        ...
    }
}

Usage:

public class TestController : ApiController
{
    [HttpGet, Route("api/test")]
    [CustomAuthorizeAttribute(AuthLevel.Client)]
    public IHttpActionResult Get()
    {
        return Ok();
    }

    [HttpGet, Route("api/testasync")]
    [CustomAuthorizeAttribute(AuthLevel.Server)]
    public async Task<IHttpActionResult> GetAsync()
    {
        return Task.FromResult(Ok());
    }
}

While there's no substitution for good integration tests, I'd like to be able to unit test that this attribute has been defined on the controller action with the correct enum value. It also has to work with both standard and async methods.

1条回答
兄弟一词,经得起流年.
2楼-- · 2019-08-16 16:56

I created a couple of extension methods for this:

public static bool HasExpectedAuthLevel<T>(this T controller, Expression<Action<T>> action, AuthLevel expectedAuthLevel)
    where T : ApiController
{
    return controller.HasAttributeWithExpectedArgument(action, typeof(CustomAuthorizeAttribute), expectedAuthLevel);
}

public static bool HasExpectedAuthLevel<T>(this T controller, Expression<Func<T, Task>> action, AuthLevel expectedAuthLevel)
    where T : ApiController
{
    return controller.HasAttributeWithExpectedArgument(action, typeof(CustomAuthorizeAttribute), expectedAuthLevel);
}

public static bool HasAttributeWithExpectedArgument<TController, TArgument>(this TController controller, Expression<Action<TController>> action, Type attributeType, TArgument expectedArgument)
    where TController : ApiController
{
    return HasAttributeWithExpectedArgument(action?.Body as MethodCallExpression, attributeType, expectedArgument);
}

public static bool HasAttributeWithExpectedArgument<TController, TArgument>(this TController controller, Expression<Func<TController, Task>> action, Type attributeType, TArgument expectedArgument)
    where TController : ApiController
{
    return HasAttributeWithExpectedArgument(action?.Body as MethodCallExpression, attributeType, expectedArgument);
}

private static bool HasAttributeWithExpectedArgument<TArgument>(MethodCallExpression action, Type attributeType, TArgument expectedArgument)
{
    if (action == null || !attributeType.IsSubclassOf(typeof(Attribute)))
    {
        return false;
    }

    var attributesData = action.Method.GetCustomAttributesData().Where(a => a.AttributeType == attributeType).ToArray();

    return attributesData.Any(attribute =>
        attribute.ConstructorArguments.Any(arg =>
            arg.ArgumentType == typeof(TArgument) && EqualityComparer<TArgument>.Default.Equals((TArgument)arg.Value, expectedArgument)));
}

Usage:

[Test]
public void Get_HasCorrectAuthLevel()
{
    var controller = new TestController();
    controller.HasExpectedAuthLevel(c => c.Get(), AuthLevel.Client).Should().BeTrue();
    controller.HasExpectedAuthLevel(c => c.GetAsync(), AuthLevel.Server).Should().BeTrue();
}
查看更多
登录 后发表回答