¿Cómo puedo probar la presencia de un filtro de acción con argumentos de constructor?

Estoy tratando de probar que mi controlador base está decorado con un cierto filtro de acción. Como el constructor de este filtro se ve en web.config , mi primer bash de prueba falla porque el proyecto de prueba no tiene un archivo de configuración válido. Pasando, utilicé TestConfigProvider que TestConfigProvider en el constructor del filtro, pero la siguiente prueba falla porque el proveedor de configuración no se pasa al constructor. ¿De qué otra manera puedo probar si este filtro se aplica?

 [TestMethod] public void Base_controller_must_have_MaxLengthFilter_attribute() { var att = typeof(BaseController).GetCustomAttribute(); Assert.IsNotNull(att); } 

Bien, has dado un buen primer paso al reconocer que Web.config es solo otra dependencia y envolverlo en ConfigProvider para inyectar es una excelente solución.

Pero te estás tropezando con uno de los problemas de diseño de MVC, es decir, que para ser compatible con DI, los atributos solo deberían proporcionar metadatos, pero nunca definir el comportamiento . Esto no es un problema con su enfoque de prueba, es un problema con el enfoque del diseño del filtro.

Como se señaló en la publicación, puede evitar este problema dividiendo su atributo de filtro de acción en 2 partes.

  1. Un atributo que no contiene ningún comportamiento para marcar sus controladores y métodos de acción.
  2. Una clase DI-friendly que implementa IActionFilter y contiene el comportamiento deseado.

El enfoque es usar IActionFilter para probar la presencia del atributo y luego ejecutar el comportamiento deseado. El filtro de acción se puede suministrar con todas las dependencias y luego inyectarse cuando se compone la aplicación.

 IConfigProvider provider = new WebConfigProvider(); IActionFilter filter = new MaxLengthActionFilter(provider); GlobalFilters.Filters.Add(filter); 

NOTA: si necesita que alguna de las dependencias del filtro tenga una vida útil más corta que el singleton, necesitará usar un GlobalFilterProvider como en esta respuesta .

La implementación de MaxLengthActionFilter se vería así:

 public class MaxLengthActionFilter : IActionFilter { public readonly IConfigProvider configProvider; public MaxLengthActionFilter(IConfigProvider configProvider) { if (configProvider == null) throw new ArgumentNullException("configProvider"); this.configProvider = configProvider; } public void OnActionExecuted(ActionExecutedContext filterContext) { var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor); if (attribute != null) { var maxLength = attribute.MaxLength; // Execute your behavior here, and use the configProvider as needed } } public void OnActionExecuting(ActionExecutingContext filterContext) { var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor); if (attribute != null) { var maxLength = attribute.MaxLength; // Execute your behavior here, and use the configProvider as needed } } public MaxLengthAttribute GetMaxLengthAttribute(ActionDescriptor actionDescriptor) { MaxLengthAttribute result = null; // Check if the attribute exists on the controller result = (MaxLengthAttribute)actionDescriptor .ControllerDescriptor .GetCustomAttributes(typeof(MaxLengthAttribute), false) .SingleOrDefault(); if (result != null) { return result; } // NOTE: You might need some additional logic to determine // which attribute applies (or both apply) // Check if the attribute exists on the action method result = (MaxLengthAttribute)actionDescriptor .GetCustomAttributes(typeof(MaxLengthAttribute), false) .SingleOrDefault(); return result; } } 

Y su atributo, que no debe contener ningún comportamiento, debería verse más o menos así:

 // This attribute should contain no behavior. No behavior, nothing needs to be injected. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)] public class MaxLengthAttribute : Attribute { public MaxLengthAttribute(int maxLength) { this.MaxLength = maxLength; } public int MaxLength { get; private set; } } 

Con un diseño más flexible, la prueba de la existencia del atributo es mucho más sencilla.

 [TestMethod] public void Base_controller_must_have_MaxLengthFilter_attribute() { var att = typeof(BaseController).GetCustomAttribute(); Assert.IsNotNull(att); } 

Quizás pueda agregar el archivo de configuración válido a su proyecto de prueba a través de “agregar archivo como enlace” enter image description hereenter image description here

Recientemente, aquí estoy cada vez más pregunta con respecto a los “problemas” de configuración. Todos tienen una base común: tienes varios proyectos, servidores, servicios que necesitan usar la misma configuración. Mi consejo para usted: deje de usar Web.config.

¡Pon toda tu configuración en la base de datos! Agregue una tabla (o tal vez varias tablas) con todas sus claves de configuración y valores y léalas cuando se inicie la aplicación (global.asax).

De esta forma, no tendrá que preocuparse por manejar su configuración en cada proyecto o inyectarla a diferentes constructores.