c#WebApi如何单元测试控制器动作是否用带有特定参数的授权属性修饰

时间:2019-03-29 14:41:31

标签: c# asp.net-web-api authorization

我们有一个自定义AuthorizationFilterAttribute,用于装饰WebApi控制器操作,该操作采用枚举参数来定义访问级别。

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)
    {
        ...
    }
}

用法:

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

虽然不能替代良好的集成测试,但我希望能够对已在控制器操作上使用正确的枚举值定义此属性的单元进行测试。它还必须同时使用标准方法和异步方法。

1 个答案:

答案 0 :(得分:0)

我为此创建了一些扩展方法:

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

用法:

[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();
}