2011-11-02 42 views
1

这个问题更多关于在SpecFlow中共享值的一般性讨论。请根据您在SpecFlow中的经验提供您可能具有的建设性反馈。对ScenarioContext.Current有用的抽象?

我对这项技术相对较新,在寻找解决方案来共享步骤定义文件中的值时,我发现了ScenarioContext.Current.GetScenarioContext.Current.Set。这些非常方便,但正如我所看到的那样,存在一些问题。

  1. 这种方法涉及到很多打字。
  2. 使用字符串索引插入和检索值类型,因此需要使用字符串常量或枚举来确保步骤定义之间的一致性。
  3. 假设您试图检索的值已被插入可能并不安全。

我想出了一个抽象概念,我认为这样做更容易一些,我想知道人们对它的看法。

问题:我的数值已设置?

我对此的解决方案是将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); 
     } 
    } 

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

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

回答

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

我使用起订量 - 非常有用的框架。