Test Fixture + GWTdo: direcionando desafios do BDD

Yan Justino
Yan Justino

--

Behavior-Driven Development(BDD) tem sido adotado por muitos times de software como uma abordagem para obter requisitos, através de especificações legíveis, com detalhes suficiente para  validar se o código produzido os implementam ou não[1]. Contudo, gerenciar especificações BDD ao longo do tempo pode ser desafiador: a complexidade das ferramentas, as dificuldades de localização de falhas em largas suites BDD, a duplicação e a necessidade de manter especificaçãoes em adição aos testes de unidade são alguns desses desafios.  Nesse post apresentaremos formas de direcionar esses desafios com o uso de Test Fixtures e da biblioteca GWTdo.

Behavior-Driven Development

uma breve definição sobre especificações em BDD

Em BDD, o comportamento (Behavior) do software é especificado como um conjunto de interações, expressos como sentenças em linguagem natural, organizadas ao redor de uma estrutura Given-When-Then(GWT) [1] [2], um pequento texto narrativo redigido do ponto de vista do usuário[3]. O trecho a seguir é um exemplo de redação GWT:

Feature: User trades stocks
Scenario: User requests a sell before close of trading
Given I have 100 shares of MSFT stock
And the time is before close of trading
When I ask to sell 20 shares of MSFT stock
Then I should have 80 shares of MSFT stock

Nesse trecho temos a indicação de uma funcionalidade (Feature) a qual um dos cenários (Scenario) descreve um usuário solicitando a venda de suas ações. As pré-condições para satisfazer esse scenário (Given) é que o usuário possua ações (MSFT) e que a hora da ordem de venda seja anterior ao horário de fechamento das negociações. Quando (When) o cenário for executado, a pós-condição esperada (Then) é que a quantidade de ações vendidas sejam "debitadas" do volume total de ações (MSFT).

Benefícios

Uma das vantagens desse forma de especificação está na sua legibilidade: uma vez que, nesse nível de abstração, se evita elementos técnicos, facilita-se a comunicação e a compreensão por todos todos. Outro aspecto positivo é como o uso da abordagem melhora a forma como o software e seu código-fonte é documentado. "A documentação é o código de trabalho"!

Desafios

Se por um lado a legibilidade e a fácil documentação do código são vantagens, gerenciar as especificações é um desafio particular: o rasteio de falhas, a duplicação de elementos da especificação e a necessidade de mante-la em adição aos testes de unidade são exemplos de problemas associados a BDD.

Talvez esses problemas residam na tentativa de manter do lado técnico a especificação em linguagem natural em arquivos apartados ou, como strings em métodos decorados.

Diante disso, a melhor solução seria ter o benfíco da legibilidade e a melhor gestão possível das especificações. Acredito que a solução envolva encontrar um certo ponto de equilíbrio: abrir mão de um pouco da legibilidade (inicialmente) em detrimento a gestão e manutenção dos testes BDD. Exploraremos isso na seção a seguir.

Utilizando o Test Fixute + GWTdo

Direcionando desafios do BDD

Mirando a ambição por encontrar o equilíbrio entre legibilidade e gestão das especificações, cheguei numa solução que me agrada e tem me acompanhado nos últimos projetos. Essa solução foi batizada de GWTdo está disponível como projeto open source e distribuída como pacote Nuget em duas versões: Gwtdo (🇬🇧) ou Gwtdo.PtBr (🇧🇷). Essa biblioteca estimula a escrita de Test Fixture, bem como oferece classes que se comportam como conjunções de frases pra gerar um efeito aproximado de uma narrativa na escrita de especificações. Apresentaremos essa solução nos exemplos a seguir.

Test Fixture

O primeiro passo de nossa abordagem é a criação de Test Fixture. O objetivo de um Test Fixture é garantir que haja um ambiente bem conhecido e fixo, no qual os testes são executados e os resultados possam ser repetidos [4].

A figura 1, ilustra nosso exemplo de Test Fixture, que é um simples record type (ver c# 9.0) implementando a interface IFixture.

Figura 1. Test Fixture

A estrutura do seu Test Fixture pode ser bem mais complexa, dependendo da sua necessidade. O exemplo da Figura 1, é uma forma mais didática e simples para que você compreenda o conceito.

Extension Methods

O próximo passo é criação de extension methods que nos auxilie com a construção das especificações através de configuração, execução e afirmação de testes (Arrange, Act and Assert) [5]. GWTdo oferece estruturas para essa construção. Afim de facilitar a legibilidade dos nossos métodos sugerimos que você antes faça uso de alias directive, como ilustrado na Figura 2.

Figura 2. Utilizando alias directive

Arrange

Configure o objeto a ser testado. A Figura 3 ilustra sua utilização de um Arrenge.

Figura 3. utilização de arrange

Para fins de teste, podem ser utilizados colaboradores como mocks, fakes, etc; ou a classe real, se você preferir.

Act

"Atue" no objeto (por meio de algum modificador). Você pode precisar fornecer parâmetros (novamente, possivelmente objetos de teste). A Figura 4 ilustra um exemplo da escrita de um Act.

Figura 4. utilização de act

Assert

Faça afirmações sobre o objeto, seus colaboradores, seus parâmetros e, possivelmente (raramente !!) o estado global. A figura 5 ilustra o uso do Assert.

Figura 5. utilização de assert

Testes

Com nosso Test Fixture preparado, podemos testar nosso código usando o os métodos de extensão (Arrange, Act e Assert). Para isso, você deve estender a classe Feature<T>, que nos possibilitará a utilização das conjunções GIVEN, WHEN e THEN. As figuras 6 e 7 exemplificam o resultado de como nossos testes ficam após a aplicação das técnicas anteriores. A figura 8 ilustra uma versão em português do teste ilustrado na figura 7.

Figura 6. Teste básico
Figura 7. Teste mais complexo
Figura 8. Versão brasileira do GWTdo PTBR

Com os exemplos anteriores é perceptível como a especificação fica bem isolada do código necessário para o teste. Isso trás uma melhor legibilidade e compreensibilidade [3] sobre o funcionamento da feature. Além disso, uso de Test Fixtures somado às estruturas da biblioteca GWTdo possibilitaram direcionar os seguintes desafios de BDD:

  • Ao abrir mão de uma epecificação isolada, optamos por unificar epecificação e testes em uma pseudo linguagem natural para ter uma melhor manutenção do código do testes. Isso não significa que uma especificação mais natural não possa ser gerada a partir desse código.
  • O uso de métodos de extensão auxiliam na reutilização de expressões da especificação, evitando assim repetições desnecessárias para uma mesma feature. Com isso, podemos adotar ferramentas da IDE para facilmente evitar essas repetições no código-fonte.
  • Cada método de extensão, quando bem escrito é uma chamada atômica na execução do teste. nesse sentido, uma falha pode ser facilmente rastreada por meio de debugs ou análise de logs erros do teste, nos quais estarão indicados o método (expressão da especificação) que falhou e a linha de código onde o erro ocorreu.

O código-fonte ilustrado nas figuras anteriores pode ser acessado na integra clicando aqui.

Conclusão

O que aprendemos nesse post

Apresentamos anteriormente alguns conceitos de BDD e GWT. Falamos também sobre os benefícos e desafios de gerenciar especificações em BDD. Além disso, ilustramos o uso da biblioteca GWTdo associada a técnica de Test Fixture para lidar com desafios da especificação BDD.

Desafios futuros

Um desafio futuro para a solução GWTdo é realizar a engenharia reversa do teste, para a criação do arquivo de especificação numa linguagem mais natural. Isso permitirá utilizar extensões na IDE/pipeline que ofereçam uma versão 100% legível da especificação.

Ficou interessado? Quer colaborar com o projeto? Acesso o repositório e veja como enviar sua contribuição.

Referências

Artigos, Livros e Websites que fundamentaram esse post

  1. L. P. Binamungu, S. M. Embury and N. Konstantinou, “Maintaining behavior driven development specifications: Challenges and opportunities,” 2018 IEEE 25th International Conference on Software Analysis, Evolution and Reengineering (SANER), Campobasso, Italy, 2018, pp. 175–184, doi: 10.1109/SANER.2018.8330207.
  2. Fowler, M., 2021. bliki: GivenWhenThen. [online] martinfowler.com. Available at: <https://martinfowler.com/bliki/GivenWhenThen.html> [Accessed 19 April 2021].
  3. Engenharia de Software — 8ª Edição. (2016). (n.p.): McGraw Hill Brasil.
  4. L. da Silva and P. Vilain, “Execution and code reuse between test classes,” in 2016 IEEE 14th International Conference on Software Engineering Research, Management and Applications (SERA), Towson, MD, USA, 2016 pp. 99–106. doi: 10.1109/SERA.2016.7516134 — keywords: {fixtures;robustness;testing;proposals;informatics;writing} — url: https://doi.ieeecomputersociety.org/10.1109/SERA.2016.7516134
  5. Wake, B., 2021. 3A — Arrange, Act, Assert — XP123. [online] Xp123.com. Available at: <https://xp123.com/articles/3a-arrange-act-assert/> [Accessed 20 April 2021].

--

--

Yan Justino
Yan Justino

MSc. Software Engineering — MCP | MCSA | MCSD | OCA