Funciones útiles de SpecFlow

Hola a todos!

En Habré hay excelentes artículos sobre SpecFlow. Quiero profundizar en este tema y hablar sobre la ejecución de pruebas en paralelo, pasando datos entre pasos, ayudantes de ayuda, transformaciones, enlaces y sobre el uso de Json como fuente de datos.

Ejecución paralela y transferencia de datos entre pasos.


En la documentación para transferir datos entre pasos, encontramos el siguiente ejemplo:

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

Este código usa claves de cadena. Memorizarlos y escribirlos es bastante tedioso.

Este problema se puede resolver creando una clase estática con las propiedades necesarias:

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

La transferencia de datos ahora se ve así:

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

Desafortunadamente, no podremos usar ScenarioContext.Current al ejecutar pruebas en paralelo hasta que realicemos la Inyección necesaria

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

Por lo tanto, resolvimos un par de problemas: nos deshicimos de las teclas de cadena y proporcionamos la capacidad de ejecutar pruebas en paralelo. Para aquellos que quieran experimentar, creé un pequeño proyecto.

Ayudantes de ayuda y transformaciones


Considera el siguiente paso

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

Muy a menudo, los datos de una tabla se restan así

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

Usando Assist Helpers, leer los datos de las pruebas se ve mucho más elegante. Necesitamos hacer un modelo con las propiedades apropiadas:

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

y usarlo en CreateInstance

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

Con Transformaciones , la descripción del paso de prueba se puede simplificar aún más.

Define la transformación:

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

Ahora podemos usar el tipo deseado como parámetro en el paso:

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

Para aquellos que quieren experimentar, el mismo proyecto.

Ganchos y uso de Json como fuente de datos de prueba


Las tablas SpecFlow son más que suficientes para datos de prueba simples. Sin embargo, hay escenarios de prueba con una gran cantidad de parámetros y / o una estructura de datos compleja. Para utilizar dichos datos de prueba, mientras se mantiene la legibilidad del script, necesitamos ganchos . Utilizaremos el gancho [BeforeScenario] para leer los datos necesarios del archivo Json. Para hacer esto, defina etiquetas especiales en el nivel del 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 

y agregue la lógica de procesamiento a los ganchos:

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

Este código resta el contenido del archivo json (ruta relativa al archivo) en una variable de cadena y lo guarda en los datos del script (ScenarioData.JsonDataSource). Por lo tanto, podemos usar estos datos cuando sea necesario

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

Como puede haber muchos datos en Json, la actualización de los datos de prueba también se puede implementar a través de etiquetas. Los interesados ​​pueden ver un ejemplo en el mismo proyecto.

Referencias


1. pepino
2. Pepinillo
3. Documentación de SpecFlow
4. Wiki SpecFlow
5. Especificación ejecutable: SpecFlow A a Z
6. Pruebas basadas en datos y SpecFlow

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


All Articles