对ScenarioContext.Current的一个有用的抽象
本文关键字:一个 有用 抽象 ScenarioContext Current | 更新日期: 2023-09-27 18:13:00
这个问题更多的是关于SpecFlow中共享值的一般性讨论。请根据您在SpecFlow中的经验提供任何建设性的反馈。
我对这项技术比较陌生,当寻找在步骤定义文件之间共享值的解决方案时,我发现了ScenarioContext.Current.Get
和ScenarioContext.Current.Set
。这些都很方便,但在我看来,有几个问题。
- 在这种方法中有相当多的输入。
- 值类型通过字符串索引插入和检索,因此需要使用字符串常量或枚举来确保步骤定义之间的一致性。
- 假设您试图检索的值已插入可能不安全。
我想出了一个抽象的概念,我认为它使一些事情更容易接受,我想知道人们是怎么想的。
问题:我的值设置了吗?
我的解决方案是将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部分,我已经使用了这样的模式,特别是用于获取被测试对象的实例,因为它节省了大量的输入&潜在错误。
如果您只需要一个类的虚拟实例,另一个有用的模式(取决于具体情况)是延迟初始化方法,而不是第一部分解决方案:
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 -非常有用的框架。
我认为你正在寻找的功能是上下文注入(依赖注入):https://github.com/techtalk/SpecFlow/wiki/Context-Injection
这允许您在步骤定义之间共享类