我正在尝试测试我的基本控制器是否装饰有特定的动作过滤器。因为此过滤器的构造函数看上去像web.config,所以我的第一个测试尝试失败,因为测试项目没有有效的配置文件。继续,我使用了TestConfigProvider我注入到过滤器构造函数中的a,但是以下测试失败了,因为未将配置提供程序传递给构造函数。我还能如何测试是否应用了此过滤器?
web.config
TestConfigProvider
[TestMethod] public void Base_controller_must_have_MaxLengthFilter_attribute() { var att = typeof(BaseController).GetCustomAttribute<MaxLengthFilter>(); Assert.IsNotNull(att); }
好的,您已经迈出了很好的第一步,认识到Web.config只是另一个依赖项,并将其包装到ConfigProvider中进行注入是一个很好的解决方案。
但是,您开始陷入MVC的设计问题之一-即,为了实现DI友好性,属性应仅提供元数据,而从不实际定义行为。这不是测试方法的问题,而是滤波器设计方法的问题。
正如文章中指出的那样,您可以通过将操作过滤器属性分为两部分来解决此问题。
该方法是使用IActionFilter来测试属性的存在,然后执行所需的行为。可以为动作过滤器提供所有依赖项,然后在组成应用程序时将其注入。
IConfigProvider provider = new WebConfigProvider(); IActionFilter filter = new MaxLengthActionFilter(provider); GlobalFilters.Filters.Add(filter);
注意: 如果您需要过滤器的任何依赖项以使生命周期短于单例,则需要GlobalFilterProvider在此答案中使用as 。
GlobalFilterProvider
MaxLengthActionFilter的实现如下所示:
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; } }
并且, 不应包含任何行为的 属性 应 如下所示:
// 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; } }
使用更宽松的耦合设计,测试属性的存在要简单得多。
[TestMethod] public void Base_controller_must_have_MaxLengthFilter_attribute() { var att = typeof(BaseController).GetCustomAttribute<MaxLengthAttribute>(); Assert.IsNotNull(att); }