unit test framework filters authorizeattribute c# unit-testing asp.net-web-api moq

c# - test - web api 2 filters



Prueba unitaria IAuthenticationFilter en WebApi 2 (2)

Sin embargo, es posible lograr lo que deseaba, ya que ninguno de los objetos en el contexto de la cadena.Request.Headers.Authorization expone las propiedades virtuales. Mock o cualquier otro framework no le proporcionará mucha ayuda. Aquí está el código para obtener HttpAuthenticationContext con valores simulados:

HttpRequestMessage request = new HttpRequestMessage(); HttpControllerContext controllerContext = new HttpControllerContext(); controllerContext.Request = request; HttpActionContext context = new HttpActionContext(); context.ControllerContext = controllerContext; HttpAuthenticationContext m = new HttpAuthenticationContext(context, null); HttpRequestHeaders headers = request.Headers; AuthenticationHeaderValue authorization = new AuthenticationHeaderValue("scheme"); headers.Authorization = authorization;

Simplemente necesita crear de forma ordinaria ciertos objetos y pasarlos a otros con constructores o propiedades. La razón por la que he creado las instancias HttpControllerContext y HttpActionContext es que la propiedad HttpAuthenticationContext.Request solo tiene una parte: su valor puede establecerse a través de HttpControllerContext. Utilizando el método anterior, puede probar su filtro, sin embargo, no puede verificar en la prueba si las propiedades específicas de los objetos anteriores se tocaron simplemente porque no son anulables, sin eso no hay posibilidad de seguir esto.

Estoy intentando probar un análisis de autenticación básica que he escrito para un proyecto de WebApi 2, pero tengo problemas para burlarme del objeto HttpAuthenticationContext requerido en la llamada OnAuthentication.

public override void OnAuthentication(HttpAuthenticationContext context) { base.OnAuthentication(context); var authHeader = context.Request.Headers.Authorization; ... the rest of my code here }

La línea en la implementación que estoy tratando de configurar para burlarse es la que establece la variable authHeader.

Sin embargo, no puedo simular el objeto Headers porque está sellado. Y no puedo simular la solicitud y establecer un encabezado burlado porque es una propiedad no virtual. Y así sucesivamente hasta la cadena todo el camino hasta el contexto.

¿Alguien ha probado con éxito una nueva implementación de IAuthenticationFilter?

Estoy usando Moq, pero estoy seguro de que podría seguirlo en cualquier biblioteca burlona si tiene código de muestra.

Gracias por cualquier ayuda.


Pude usar la respuesta de @ mr100 para ayudarme a resolver mi problema, que consistía en probar un par de implementaciones de IAuthorizationFilter. Para poder probar con eficacia la autorización de API web, no se puede usar AuthorizationFilterAttribute y se debe aplicar un filtro global que verifique la presencia de atributos pasivos en los controladores / acciones. Para resumir, amplié la respuesta de @ mr100 para incluir simulaciones para los descriptores de controlador / acción que le permiten probar con / sin la presencia de sus atributos. A modo de ejemplo, incluiré el más simple de los dos filtros que necesitaba para probar la unidad, lo que fuerza las conexiones HTTPS para controladores / acciones especificados (o globalmente si lo desea):

Este es el atributo que se aplica donde quiera que quiera forzar una conexión HTTPS, tenga en cuenta que no hace nada (es pasivo):

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class HttpsRequiredAttribute : Attribute { public HttpsRequiredAttribute () { } }

Este es el filtro que en cada solicitud comprueba si el atributo está presente y si la conexión es HTTPS o no:

public class HttpsFilter : IAuthorizationFilter { public bool AllowMultiple => false; public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) { List<HttpsRequiredAttribute> action = actionContext.ActionDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList(); List<HttpsRequiredAttribute> controller = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList(); // if neither the controller or action have the HttpsRequiredAttribute then don''t bother checking if connection is HTTPS if (!action.Any() && !controller.Any()) return continuation(); // if HTTPS is required but the connection is not HTTPS return a 403 forbidden if (!string.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase)) { return Task.Factory.StartNew(() => new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden) { ReasonPhrase = "Https Required", Content = new StringContent("Https Required") }); } return continuation(); } }

Y finalmente, una prueba para demostrar que devuelve un estado de 403 prohibido cuando se requiere https pero no se usa (usando una gran cantidad de la respuesta de @ mr100 aquí):

[TestMethod] public void HttpsFilter_Forbidden403_WithHttpWhenHttpsIsRequiredByAction() { HttpRequestMessage requestMessage = new HttpRequestMessage(); requestMessage.SetRequestContext(new HttpRequestContext()); requestMessage.RequestUri = new Uri("http://www.some-uri.com"); // note the http here (not https) HttpControllerContext controllerContext = new HttpControllerContext(); controllerContext.Request = requestMessage; Mock<HttpControllerDescriptor> controllerDescriptor = new Mock<HttpControllerDescriptor>(); controllerDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>()); // empty collection for controller Mock<HttpActionDescriptor> actionDescriptor = new Mock<HttpActionDescriptor>(); actionDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>() { new HttpsRequiredAttribute() }); // collection has one attribute for action actionDescriptor.Object.ControllerDescriptor = controllerDescriptor.Object; HttpActionContext actionContext = new HttpActionContext(); actionContext.ControllerContext = controllerContext; actionContext.ActionDescriptor = actionDescriptor.Object; HttpAuthenticationContext authContext = new HttpAuthenticationContext(actionContext, null); Func<Task<HttpResponseMessage>> continuation = () => Task.Factory.StartNew(() => new HttpResponseMessage() { StatusCode = HttpStatusCode.OK }); HttpsFilter filter = new HttpsFilter(); HttpResponseMessage response = filter.ExecuteAuthorizationFilterAsync(actionContext, new CancellationTokenSource().Token, continuation).Result; Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode); }