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<IContextManager>().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<IContextManager>().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<string>("ScenarioContext");
        var featureContextText  = this.FeatureContext.Get<string>("FeatureContext");
        var stepContextText    = this.StepContext.Get<string>("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/

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo