我有这样的印象,似乎不是这样的。当我单独运行三个集成测试时,它们都能够成功,但是并行运行时,我遇到了System.ArgumentException: An item with the same key has already been added.
我原本以为ScenarioContext.Current
总是指向正确的场景,但现在看来好像出现了混淆。是否有人已经成功地将线程安全添加到这个类中?或者我应该使用另一种方法来在步骤文件之间共享值呢?
我有这样的印象,似乎不是这样的。当我单独运行三个集成测试时,它们都能够成功,但是并行运行时,我遇到了System.ArgumentException: An item with the same key has already been added.
我原本以为ScenarioContext.Current
总是指向正确的场景,但现在看来好像出现了混淆。是否有人已经成功地将线程安全添加到这个类中?或者我应该使用另一种方法来在步骤文件之间共享值呢?
ScenarioContext.Curent源:
public static ScenarioContext Current
{
get
{
if (current == null)
{
Debug.WriteLine("Accessing NULL ScenarioContext");
}
return current;
}
internal set { current = value; }
}
正如您所看到的,它不是线程安全的。 https://github.com/techtalk/SpecFlow/blob/master/Runtime/ScenarioContext.cs
[Binding]
public class StepsWithScenarioContext : Steps
{
[Given(@"I put something into the context")]
public void GivenIPutSomethingIntoTheContext()
{
this.ScenarioContext.Set("test-value", "test-key");
}
}
我知道这是一个旧帖子,但它引用得很好,所以这是我的解决方案:
只需像这样替换ScenarioContext为自定义实现:
public class ScenarioContextSafe
{
private static ScenarioContextSafe _current;
private static readonly object Locker = new object();
public static ScenarioContextSafe Current
{
get
{
lock (Locker) {
return _current ?? (_current = new ScenarioContextSafe());
}
}
}
public static void Reset()
{
lock (Locker) {
_current = null;
}
}
private readonly ConcurrentDictionary<string, object> _concurrentDictionary = new ConcurrentDictionary<string, object>();
public void Add(string key, object value)
{
_concurrentDictionary.TryAdd(key, value);
}
public void Set(object value, string key)
{
if (!_concurrentDictionary.ContainsKey(key))
_concurrentDictionary.TryAdd(key, value);
else
_concurrentDictionary[key] = value;
}
public void Remove(string key)
{
object result;
_concurrentDictionary.TryRemove(key, out result);
}
public T Get<T>(string key)
{
object result;
_concurrentDictionary.TryGetValue(key, out result);
return (T)result;
}
public bool ContainsKey(string key)
{
return _concurrentDictionary.ContainsKey(key);
}
public void Pending()
{
ScenarioContext.Current.Pending();
}
public ScenarioInfo ScenarioInfo{
get { return ScenarioContext.Current.ScenarioInfo; }
}
}
然后,在每个场景之前创建一个重置上下文的钩子
[BeforeScenario()]
public static void BeforeAllScenario()
{
ScenarioContextSafe.Reset();
}
我希望这能帮助到某个人。
FeatureContext
和上下文注入可能会解决一些并发问题,但是在绑定之间共享仍然成为一个问题,特别是当步骤绑定更通用且与任何特定功能无关时。 - Samo