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