ScenarioContext.Current上有用的抽象?

时间:2011-11-02 13:33:58

标签: c# specflow data-sharing

这个问题更多是关于在SpecFlow中共享值的一般性讨论。请根据您在SpecFlow中的经验提供您可能提供的任何建设性反馈。

我对这项技术比较陌生,在寻找在步骤定义文件之间共享价值的解决方案时,我找到了ScenarioContext.Current.GetScenarioContext.Current.Set。这些非常方便,但正如我所看到的,存在一些问题。

  1. 这种方法涉及很多类型。
  2. 使用字符串索引插入和检索值类型,因此需要使用字符串常量或枚举来确保步骤定义之间的一致性。
  3. 假设您尝试检索的值已插入,可能不安全。
  4. 我想出了一个抽象概念,我认为这有点容易让人感觉更容易接受,我想知道人们怎么想。

    问题:我的价值已设定?

    我的解决方案是将ScenarioContext.Current包装在单例访问器类中。此类的行为与ScenarioContext.Current类似,只是在无法找到值时抛出AssertInconclusiveException

        private static ScenarioContextAccessor instance;
    
        public static ScenarioContextAccessor Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ScenarioContextAccessor();
                }
    
                return instance;
            }
        }
    
        private ScenarioContextAccessor() { }
    
        public T Retrieve<T>(string index)
        {
            try
            {
                T val = (T)ScenarioContext.Current[index];
                if (val == null)
                {
                    throw new Exception();
                }
    
                return val;
            }
            catch
            {
                throw new AssertInconclusiveException(index + " of type " + typeof(T).Name + " was not found in the current scenario context. Did you execute your steps out of order?");
            }
        }
    
        public T Retrieve<T>()
        {
            try
            {
                T val = ScenarioContext.Current.Get<T>();
                if (val == null)
                {
                    throw new Exception();
                }
    
                return val;
            }
            catch
            {
                throw new AssertInconclusiveException("No object of type " + typeof(T).Name+ " could be found in the current scenario context. Did you execute your steps out of order?");
            }
        }
    
        public void Set(string index, object value)
        {
            ScenarioContext.Current[index.ToLower(CultureInfo.InvariantCulture)] = value;
        }
    
        public void Set<T>(T value)
        {
            ScenarioContext.Current.Set<T>(value);
        }
    }
    

    问题:这需要输入太多内容!

    我的解决方案是让任何步骤定义要求这些值将它们定义为由ScenarioContextAccessor备份的私有属性。访问值类型的任何属性都使用字符串常量作为索引。

        private string FolderName
        {
            get
            {
                return ScenarioContextAccessor.Instance.Retrieve<string>(FolderingScenarioContextKey.FolderName);
            }
            set
            {
                ScenarioContextAccessor.Instance.Set(FolderingScenarioContextKey.FolderName, value);
            }
        }
    
        private UserDocumentMetadata Metadata
        {
            get
            {
                return ScenarioContextAccessor.Instance.Retrieve<UserDocumentMetadata>();
            }
            set
            {
                ScenarioContextAccessor.Instance.Set<UserDocumentMetadata>(value);
            }
        }
    

    所以现在我可以像访问简单的属性一样轻松访问我的共享值。

    请提供您可能提供的任何建设性反馈。谢谢!

2 个答案:

答案 0 :(得分:0)

至于第1部分,我不同意。 如果没有为数据设置正确,我发现测试失败更有用。

至于第2部分,我已经使用了这样的模式,特别是为了获得被测试对象的实例,因为它节省了大量的输入和输入。潜在的错误。

如果你只需要一个类的虚拟实例,那么另一种模式(取决于环境)而不是你的第1部分解决方案是一种懒惰的初始化方法:

public static Mock<T> GetOrMockAndStore<T>() where T : class
    {
        Mock<T> output;
        if (ScenarioContext.Current.TryGetValue(out output))
        {
            return output;
        }
        else
        {
            output = new Mock<T>();
            ScenarioContext.Current.Set(output);
        }
        return card;
    }

我正在使用Moq - 非常有用的框架。

答案 1 :(得分:0)

我认为您正在寻找的功能是Context Injection(依赖注入): https://github.com/techtalk/SpecFlow/wiki/Context-Injection

这使您可以在步骤定义之间共享类。