如何使用构造函数参数测试Action Filter的存在?

时间:2014-12-02 08:47:07

标签: c# .net asp.net-mvc

我正在尝试测试我的基本控制器是否装饰了某个动作过滤器。因为这个过滤器的构造函数看起来是web.config,所以我第一次尝试测试失败,因为测试项目没有有效的配置文件。继续,我使用了TestConfigProvider我注入到过滤器构造函数中,但是以下测试失败,因为配置提供程序未传递给构造函数。我还能测试是否应用了这个过滤器?

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

3 个答案:

答案 0 :(得分:26)

嗯,您已经迈出了良好的第一步,认识到Web.config只是另一个依赖项并将其包装到ConfigProvider中以进行注入是一个很好的解决方案。

但是,你正在惹恼MVC的一个设计问题 - 即,对DI友好,属性应该只提供元数据,但是never actually define behavior。这不是您的测试方法的问题,这是过滤器设计方法的一个问题。

正如帖子中所指出的,您可以通过将动作过滤器属性分为两部分来解决此问题。

  1. 不包含用于标记控制器和操作方法的行为的属性。
  2. 一个DI友好的类,它实现IActionFilter并包含所需的行为。
  3. 方法是使用IActionFilter测试属性的存在,然后执行所需的行为。可以为操作过滤器提供所有依赖项,然后在组合应用程序时注入。

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

    注意:如果您需要任何过滤器的依赖项,其生命周期短于单身,则需要使用this answer中的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);
    }
    

答案 1 :(得分:-1)

也许你可以通过“添加文件作为链接”将有效的配置文件添加到测试项目中 enter image description here enter image description here

答案 2 :(得分:-2)

最近我在这里越来越多关于配置“问题”的问题。它们都有一个共同的基础 - 你有几个需要使用相同配置的项目,服务器和服务。我建议你 - 停止使用Web.config。

将所有配置放入数据库! 添加一个表(或可能是几个表),其中包含所有配置键的值,并在应用程序启动时读取它们(global.asax)。

这样您就不必担心将配置复制到每个项目或将其注入不同的构造函数。