Specflow v3 ScenarioContext.Current、FeatureContext.Current or ScenarioStepContext.Current 已过时的解决方案

Specflow 提供了 ScenarioContext.Current, FeatureContext.Current or ScenarioStepContext.Current 静态成员让我们使用,Specflow 3 之后它们已经被标记过时(Obsolete),为了以后相容性的还是别用了,那要改用什么呢...


开发环境

  • VS 2017 Enterprise 15.9.11
  • Specflow 3.0.213

问题描述

ScenarioContext.Current, FeatureContext.Current or ScenarioStepContext.Current 已过时

反编译 ScenarioContext.Current,会看到这个例外描述,它说不能使用 multi thread execution,静态属性要跑 multi thread ,必须要考虑状态共用的问题,估计是这样所以不支持。

打从旧版本 https://specflow.org/documentation/Parallel-Execution 就已经不支持静态成员,估计是这一版加入了 Obsolete,提醒不要再用了

改用 Thread-safe 的 Context

有两种方式可以使用 Thread-safe 的 Context

第一种是 Context- Injection,在 *.Step.cs 使用建构函数 (ScenarioContext scenarioContext, FeatureContext featureContext),ScenarioStepContext 则透过 ScenarioContext 取得

[Binding]
[Scope(Feature = "注入Context")]
public class 注入ContextSteps
{
    private ScenarioStepContext _stepContext;
 
    private FeatureContext FeatureContext { get; }
 
    private ScenarioContext ScenarioContext { get; }
 
    public ScenarioStepContext StepContext
    {
        //get => this._stepContext ?? (this._stepContext = this.ScenarioContext.StepContext);
        get => this._stepContext ?? (this._stepContext = this.ScenarioContext.ScenarioContainer.Resolve().StepContext);
        set => this._stepContext = value;
    }
 
    public 注入ContextSteps(ScenarioContext scenarioContext, FeatureContext featureContext)
    {
        this.ScenarioContext = scenarioContext;
        this.FeatureContext  = featureContext;
    }
}

另外一种就是继承 Steps

ScenarioStepContext 还是得透过 ScenarioContext 取得,这里提供了另外一种取得方式

[Binding]
[Scope(Feature = "实践Steps")]
public class 实践StepsSteps : Steps
{
    private ScenarioStepContext _stepContext;
 
    public ScenarioStepContext StepContext
    {
        get => this._stepContext ?? (this._stepContext = this.ScenarioContext.StepContext);
 
        //get => this._stepContext ?? (this._stepContext =this.ScenarioContext.ScenarioContainer.Resolve().StepContext);
        set => this._stepContext = value;
    }
}

如此一来便能够在 Steps 使用 Thread-safe 的 ScenarioContext 、FeatureContext、ScenarioStepContext;还有一点,这样别的 Step.cs 还是能存取到状态唷

建立了一个 GlobalStepDefinition 对象,用 ScenarioContext 、FeatureContext、ScenarioStepContext 分别各自写入状态

[Binding]
public sealed class GlobalStepDefinition : Steps
{
    private ScenarioStepContext _stepContext;
 
    public ScenarioStepContext StepContext
    {
        get => this._stepContext ?? (this._stepContext = this.ScenarioContext.StepContext);
        set => this._stepContext = value;
    }
 
    [BeforeStep]
    public void BeforeStep()
    {
        this.StepContext.Set("Global", "StepContext");
    }
 
    [BeforeScenario]
    public void BeforeTest()
    {
        this.ScenarioContext.Set("Global", "ScenarioContext");
        this.FeatureContext.Set("Global", "FeatureContext");
    }
}

然后在注入 ContextSteps.cs 取出来,观察有没有值

[Binding]
[Scope(Feature = "注入Context")]
public class 注入ContextSteps
{
    ...
 
    public 注入ContextSteps(ScenarioContext scenarioContext, FeatureContext featureContext)
    {
        this.ScenarioContext = scenarioContext;
        this.FeatureContext  = featureContext;
 
        var scenarioContextText = this.ScenarioContext.Get("ScenarioContext");
        var featureContextText  = this.FeatureContext.Get("FeatureContext");
        var stepContextText    = this.StepContext.Get("StepContext");
 
        Console.WriteLine(scenarioContextText);
        Console.WriteLine(featureContextText);
        Console.WriteLine(stepContextText);
    }
}

项目位置

https://github.com/yaochangyu/sample.dotblog/tree/master/Test/Specflow3/Lab.SafeContext

参考数据

https://specflow.org/documentation/Parallel-Execution/

https://specflow.org/documentation/Context-Injection/

若有谬误,烦请告知,新手发帖请多包涵

2010~2017 C# 第四季