Caractéristiques SpecFlow utiles

Bonjour à tous!

Sur Habré, il y a d'excellents articles sur SpecFlow. Je veux approfondir ce sujet et parler de l'exécution de tests en parallèle, de la transmission de données entre les étapes, des assistants, des transformations, des hooks et de l'utilisation de Json comme source de données.

Exécution parallèle et transfert de données entre les étapes


Dans la documentation pour le transfert de données entre les étapes, nous trouvons l'exemple suivant:

// 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; 

Ce code utilise des clés de chaîne. Les mémoriser et les taper est assez fastidieux.

Ce problème peut être résolu en créant une classe statique avec les propriétés nécessaires:

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

Le transfert de données ressemble maintenant à ceci:

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

Malheureusement, nous ne pourrons pas utiliser ScenarioContext.Current lors de l'exécution de tests en parallèle tant que nous n'aurons pas effectué l'injection nécessaire.

 //      [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(); } } 

Ainsi, nous avons résolu quelques problèmes: nous nous sommes débarrassés des clés de chaîne et avons fourni la possibilité d'exécuter des tests en parallèle. Pour ceux qui veulent expérimenter, j'ai créé un petit projet.

Aider les assistants et les transformations


Considérez la prochaine étape

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

Assez souvent, les données d'une table sont soustraites comme ceci

 [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"]; ... } 

À l'aide des assistants, la lecture des données de test semble beaucoup plus élégante. Nous devons faire un modèle avec les propriétés appropriées:

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

et l'utiliser dans CreateInstance

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

Avec Transformations , la description de l'étape de test peut être encore simplifiée.

Définissez la transformation:

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

Maintenant, nous pouvons utiliser le type souhaité comme paramètre dans l'étape:

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

Pour ceux qui veulent expérimenter - le même projet.

Crochets et utilisation de Json comme source de données de test


Les tables SpecFlow sont plus que suffisantes pour des données de test simples. Cependant, il existe des scénarios de test avec un grand nombre de paramètres et / ou une structure de données complexe. Afin d'utiliser ces données de test, tout en maintenant la lisibilité du script, nous avons besoin de Hooks . Nous utiliserons le hook [BeforeScenario] pour lire les données nécessaires du fichier Json. Pour ce faire, définissez des balises spéciales au niveau du script

 @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 

et ajoutez la logique de traitement aux crochets:

 [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; } 

Ce code soustrait le contenu du fichier json (chemin d'accès relatif au fichier) dans une variable de chaîne et l'enregistre dans les données de script (ScenarioData.JsonDataSource). Ainsi, nous pouvons utiliser ces données si nécessaire

 [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); } 

Comme il peut y avoir beaucoup de données dans Json, la mise à jour des données de test peut également être implémentée via des balises. Les personnes intéressées peuvent voir un exemple dans le même projet.

Les références


1. Concombre
2. Cornichon
3. Documentation SpecFlow
4. Wiki SpecFlow
5. Spécification exécutable: SpecFlow A à Z
6. Tests basés sur les données et SpecFlow

Source: https://habr.com/ru/post/fr449574/


All Articles