Como posso testar a presença de um filtro de ação com argumentos de construtor?

Estou tentando testar se meu controlador de base está decorado com um determinado filtro de ação. Como o construtor desse filtro procura o web.config , minha primeira tentativa de teste falha porque o projeto de teste não possui um arquivo de configuração válido. Seguindo em frente, usei um TestConfigProvider que TestConfigProvider no construtor de filtro, mas o teste a seguir falha porque o provedor de configuração não é passado para o construtor. De que outra forma posso testar se este filtro é aplicado?

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

Bem, você deu um bom primeiro passo ao reconhecer que o Web.config é apenas uma outra dependência e envolvê-lo em um ConfigProvider para injetar é uma excelente solução.

Mas, você está se deparando com um dos problemas de design do MVC – ou seja, que, para ser compatível com o DI, os atributos devem fornecer apenas metadados, mas nunca definem o comportamento . Isso não é um problema com sua abordagem de teste, é um problema com a abordagem do design do filtro.

Como apontado no post, você pode contornar esse problema dividindo seu atributo de filtro de ação em duas partes.

  1. Um atributo que não contém nenhum comportamento para sinalizar seus controladores e methods de ação.
  2. Uma class amigável ao DI que implementa IActionFilter e contém o comportamento desejado.

A abordagem é usar o IActionFilter para testar a presença do atributo e, em seguida, executar o comportamento desejado. O filtro de ação pode ser fornecido com todas as dependencies e depois injetado quando o aplicativo é composto.

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

OBSERVAÇÃO: Se você precisar que qualquer uma das dependencies do filtro tenha um tempo de vida menor que o singleton, será necessário usar um GlobalFilterProvider como nesta resposta .

A implementação do MaxLengthActionFilter seria algo parecido com isto:

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

E o seu atributo que não deve conter nenhum comportamento deve ser algo como isto:

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

Com um design mais solto, o teste da existência do atributo é muito mais simples.

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

Talvez você possa adicionar o arquivo de configuração válido ao seu projeto de teste via “adicionar arquivo como link” insira a descrição da imagem aquiinsira a descrição da imagem aqui

Recentemente eu aqui mais e mais pergunta sobre “problemas” de configuração. Todos eles têm uma base comum – você tem vários projetos, servidores, serviços que precisam usar a mesma configuração. Meu conselho para você – pare de usar o Web.config.

Coloque toda a sua configuração no database! Adicione uma tabela (ou talvez várias tabelas) com todas as suas chaves de configuração e leia-as quando o aplicativo for iniciado (global.asax).

Dessa forma, você não precisa se preocupar em lidar com sua configuração em cada projeto ou injetá-la em diferentes construtores.