Recursos úteis do SpecFlow

Olá pessoal!

Em Habré, existem excelentes artigos sobre o SpecFlow. Desejo aprofundar-me neste tópico e falar sobre a execução de testes em paralelo, passando dados entre etapas, ajudando auxiliares, transformações, ganchos e sobre o uso do Json como fonte de dados.

Execução paralela e transferência de dados entre etapas


Na documentação para transferir dados entre as etapas, encontramos o seguinte exemplo:

// 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 chaves de seqüência de caracteres. Memorizar e digitá-los é bastante entediante.

Esse problema pode ser resolvido criando uma classe estática com as propriedades necessárias:

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

A transferência de dados agora se parece com isso:

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

Infelizmente, não poderemos usar o ScenarioContext.Current ao executar testes em paralelo até que façamos a injeção necessária

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

Assim, resolvemos alguns problemas: nos livramos das chaves de string e fornecemos a capacidade de executar testes em paralelo. Para quem quer experimentar, criei um pequeno projeto.

Auxiliar ajudantes e transformações


Considere o próximo passo

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

Frequentemente, os dados de uma tabela são subtraídos dessa maneira

 [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 os Assistentes de assistência, a leitura dos dados de teste parece muito mais elegante. Precisamos criar um modelo com as propriedades apropriadas:

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

e use-o em CreateInstance

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

Com Transformações , a descrição da etapa do teste pode ser simplificada ainda mais.

Defina a transformação:

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

Agora podemos usar o tipo desejado como parâmetro na etapa:

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

Para quem quer experimentar - o mesmo projeto.

Hooks e usando Json como fonte de dados de teste


As tabelas SpecFlow são mais que suficientes para dados de teste simples. No entanto, existem cenários de teste com um grande número de parâmetros e / ou uma estrutura de dados complexa. Para usar esses dados de teste, mantendo a legibilidade do script, precisamos de Hooks . Usaremos o gancho [BeforeScenario] para ler os dados necessários no arquivo Json. Para fazer isso, defina tags especiais no nível do 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 

e adicione a lógica de processamento aos 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; } 

Esse código subtrai o conteúdo do arquivo json (caminho relativo para o arquivo) em uma variável de seqüência de caracteres e o salva nos dados do script (ScenarioData.JsonDataSource). Assim, podemos usar esses dados quando necessário

 [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 pode haver muitos dados no Json, a atualização dos dados de teste também pode ser implementada por meio de tags. Os interessados ​​podem ver um exemplo no mesmo projeto.

Referências


1. Pepino
2. Maxixe
3. Documentação SpecFlow
4. Wiki do SpecFlow
5. Especificação executável: SpecFlow A a Z
6. Testes orientados a dados e SpecFlow

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


All Articles