有用的SpecFlow功能

大家好!

在Habré上有关于SpecFlow的出色文章。 我想深入探讨这个主题,并讨论并行运行测试,在步骤之间传递数据,辅助助手,转换,挂钩以及使用Json作为数据源。

步骤之间的并行执行和数据传输


在步骤之间传输数据的文档中,我们找到以下示例:

// Loading values into ScenarioContext ScenarioContext.Current["id"] = "value"; ScenarioContext.Current["another_id"] = new ComplexObject(); // Retrieving values from ScenarioContext var id = ScenarioContext.Current["id"]; var complexObject = ScenarioContext.Current["another_id"] As ComplexObject; 

此代码使用字符串键。 记住并键入它们是很乏味的。

通过创建具有必要属性的静态类可以解决此问题:

 public static class ScenarioData { public static ComplexObject Complex { get => ScenarioContext.Current.Get<ComplexObject>(nameof(Complex)); set => ScenarioContext.Current.Set(value, nameof(Complex)); } } 

数据传输现在看起来像这样:

 // Loading values into ScenarioContext ScenarioData.Complex = new ComplexObject(); // Retrieving values from ScenarioContext var complexObject = ScenarioData.Complex; 

不幸的是,在并行运行测试之前,我们将无法使用ScenarioContext.Current,除非我们进行必要的注入

 //      [Binding] public abstract class ScenarioSteps { protected ScenarioSteps(ScenarioContext scenarioContext, FeatureContext featureContext) { FeatureContext = featureContext; ScenarioContext = scenarioContext; ScenarioData = new ScenarioData(scenarioContext); } public FeatureContext FeatureContext { get; } public ScenarioContext ScenarioContext { get; } public ScenarioData ScenarioData { get; } } //  ScenarioData public class ScenarioData { private readonly ScenarioContext _context; public ScenarioData(ScenarioContext context) { _context = context; } public ComplexObject Complex { get => _context.Get<ComplexObject>(nameof(Complex)); set => _context.Set(value, nameof(Complex)); } } //      [Binding] public class ActionSteps : ScenarioSteps { public ActionSteps(ScenarioContext scenarioContext, FeatureContext featureContext) : base(scenarioContext, featureContext) { } [When(@"user uses complex object")] public void WhenUserUsesComplexObject() { ScenarioData.Complex = new ComplexObject(); } } 

因此,我们解决了两个问题:摆脱了字符串键,并提供了并行运行测试的功能。 对于那些想尝试的人,我创建了一个小项目。

协助助手和转变


考虑下一步

 When user starts rendering | SourceType | PageOrientation | PageMediaSizeName | | set-01-valid | Landscape | A4 | 

经常这样从表中减去数据

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(Table table) { var sourceType = table.Rows.First()["SourceType"]; var pageOrientation = table.Rows.First()["PageOrientation"]; var pageMediaSizeName = table.Rows.First()["PageMediaSizeName"]; ... } 

使用Assist Helper,读取测试数据看起来更加优雅。 我们需要创建一个具有适当属性的模型:

 public class StartRenderingRequest { public string SourceType { get; set; } public string PageMediaSizeName { get; set; } public string PageOrientation { get; set; } } 

并在CreateInstance中使用它

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(Table table) { var request = table.CreateInstance<StartRenderingRequest>(); ... } 

使用Transformations ,可以进一步简化测试步骤的描述。

定义转换:

 [Binding] public class Transforms { [StepArgumentTransformation] public StartRenderingRequest StartRenderingRequestTransform(Table table) { return table.CreateInstance<StartRenderingRequest>(); } } 

现在,我们可以在步骤中使用所需的类型作为参数:

 [When(@"user starts Rendering")] public async Task WhenUserStartsRendering(StartRenderingRequest request) { // we have implemented transformation, so we use StartRenderingRequest as a parameter ... } 

对于那些想尝试的人- 同一项目。

挂钩并使用Json作为测试数据的来源


对于简单的测试数据,SpecFlow表已绰绰有余。 但是,有些测试方案具有大量参数和/或复杂的数据结构。 为了使用这样的测试数据,同时保持脚本的可读性,我们需要Hooks 。 我们将使用[BeforeScenario]挂钩从Json文件中读取必要的数据。 为此,请在脚本级别定义特殊标签

 @jsonDataSource @jsonDataSourcePath:DataSource\FooResponse.json Scenario: Validate Foo functionality Given user has access to the Application Service When user invokes Foo functionality | FooRequestValue | | input | Then Foo functionality should complete successfully 

并将处理逻辑添加到钩子中:

 [BeforeScenario("jsonDataSource")] public void BeforeScenario() { var tags = ScenarioContext.ScenarioInfo.Tags; var jsonDataSourcePathTag = tags.Single(i => i.StartsWith(TagJsonDataSourcePath)); var jsonDataSourceRelativePath = jsonDataSourcePathTag.Split(':')[1]; var jsonDataSourcePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, jsonDataSourceRelativePath); var jsonRaw = File.ReadAllText(jsonDataSourcePath); ScenarioData.JsonDataSource = jsonRaw; } 

此代码将json文件的内容(文件的相对路径)减去到一个字符串变量中,并将其保存到脚本数据(ScenarioData.JsonDataSource)中。 因此,我们可以在需要时使用此数据

 [Then(@"Foo functionality should complete successfully")] public void ThenFooFunctionalityShouldCompleteSuccessfully() { var actual = ScenarioData.FooResponse; var expected = JsonConvert.DeserializeObject<FooResponse>(ScenarioData.JsonDataSource); actual.FooResponseValue.Should().Be(expected.FooResponseValue); } 

由于Json中可能有很多数据,因此也可以通过标签来实现测试数据的更新。 有兴趣的人可以在同一项目中查看示例

参考文献


1. 黄瓜
2. 小黄瓜
3. SpecFlow文档
4. SpecFlow Wiki
5. 可执行规范:SpecFlow A至Z
6. 数据驱动测试和SpecFlow

Source: https://habr.com/ru/post/zh-CN449574/


All Articles