Desenvolvimento Orientado ao Comportamento (BDD) na prática

Entenda como são aplicados os conceitos de BDD na plataforma de e-commerce da Netshoes, e como o Clean Architecture (Pattern) e o Spock Framework combinam com este conceito na prática.

Lucas Augusto
luizalabs

--

Introdução

Desde a concepção da nova plataforma de e-commerce criada pelo time de desenvolvimento da Netshoes, um conceito que ajudou muito a preservar a consistência das regras de negócio e a garantir a qualidade de código, foi o Desenvolvimento Orientado ao Comportamento conhecido como BDD (do inglês, Behavior-driven development).
Essa é uma das práticas que nosso time de desenvolvimento adotou para garantir que o comportamento das regras de negócio seja pensado em primeiro lugar.
E para garantir isso, a arquitetura da aplicação é desenhada com base em um padrão de projeto conhecido como Clean Architecture, combinado com o Spock Framework.

É claro que não foi tão simples assim implementar esse conceito logo no início do projeto. Algumas aplicações até nasceram um pouco fora do padrão, mas nada que um refactoring não resolva 😉👍. O importante é que gradualmente todos foram entendendo seu valor, e aderindo essa prática que hoje faz parte da identidade dos projetos durante a fase de desenvolvimento .

O padrão Clean Architecture é uma forma de organizar o software em camadas (assim como muitos outros padrões) sendo uma camada específica focada no tratamento de regras de negócio.
Não vamos entrar em detalhes de como é a organização dos nossos projetos, mas é importante neste ponto entender que cada fluxo de negócio
é representado por um UseCase.
O UseCase encapsula as regras de negócio de um fluxo específico e consequentemente, gerando valor.

Para saber mais sobre Clean Architecture, você pode consultar o artigo escrito por Robert C. Martin aqui neste link: The Clean Architecture

Spock Framework

O Spock é um framework para desenvolvimento de testes escrito em Groovy e que roda na JVM.
Isso significa que nos permite escrever testes para qualquer linguagem suportada pela JVM. Com uma sintaxe simples e fácil de aprender, é possível fazer várias combinações de testes, desde unitários até multi-variáveis.
Mas o que mais nos interessa aqui é a forma como o Spock estrutura as sessões de um teste, utilizando o conceito de blocos que podemos ver no exemplo abaixo.

def "Scenario 1"() {
given: "an empty bank account"
// code goes here

when: "the account is credited $10"
// code goes here

then: "the account's balance is $10"
// code goes here
}

A estrutura do cenário de teste baseado no BDD

Quando estamos descrevendo um cenário, estamos ilustrando em uma linguagem de alto nível, um aspecto específico de comportamento da aplicação. No BDD a estrutura básica de um cenário é guiada pelos blocos given-when-then (em português, dado que-quando-então), que auxiliam na construção da sentença.
Vamos ver alguns exemplos práticos de como construir cenários.

Cenário 1: A senha do usuário deve ser alterada quando solicitado
Dado um usuário logado como leonidas@sparta.gr
E deseja trocar sua senha para 9A8B12ZA
Quando ele solicita a troca para esta nova senha
Então a senha é alterada com sucesso

No exemplo acima, temos um cenário bem simples e que não aparenta ser muito difícil de desenvolver. Mas agora vamos adicionar algumas regras importantes para o negócio ao nosso fluxo de alteração de senha.

Vamos dizer que o usuário não pode colocar uma senha igual a senha atual e que a nova senha também precisa ter no mínimo 8 caracteres compostos por letras e números.

Baseado nesses novas regras, podemos ver que nosso primeiro cenário está aderente e por esse motivo a senha foi alterada com sucesso. Mas precisamos agora criar todos os cenários necessários para validar cada uma das novas regras de negócio. Então vamos lá.

Cenário 2: A nova senha não deve ser igual a atual
Dado um usuário logado como leonidas@sparta.gr
E deseja trocar sua senha aAb12345 pela mesma senha
Quando ele solicita a troca para por uma senha igual a atual
Então a senha não é alterada
E o usuário recebe uma mensagem de erro informando que a senha
não pode ser igual a anterior.
Cenário 3: A nova senha deve conter no mínimo 8 caracteres
Dado um usuário logado como leonidas@sparta.gr
E deseja trocar sua senha para 12345A com menos de 8 caracteres
Quando ele solicita a troca para esta nova senha
Então a senha não é alterada
E o usuário recebe uma mensagem de erro informando que a senha
precisa ter no mínimo 8 caracteres.
Cenário 4: A nova senha deve ser composta por números e letras
Dado um usuário logado como leonidas@sparta.gr
E deseja trocar sua senha para 12345678 utilizando apenas números
Quando ele solicita a troca para esta nova senha
Então a senha não é alterada
E o usuário recebe uma mensagem de erro informando que a senha
deve ser composta por números e letras.

Para atender a todos os novos requisitos, tivemos que criar pelo menos 4 cenários, em que nós temos o fluxo principal (caminho feliz), e os fluxos alternativos que modificam a saída conforme esbarram nas regras de negócio.

Note que já temos uma visão muito mais clara do comportamento do nosso caso de uso antes mesmo de iniciar o desenvolvimento, e essa documentação pode ser definida como um critério de aceite para iniciarmos o desenvolvimento e, também, usada para a criação dos nossos cenários de testes.

A arquitetura dos micro-serviços

Quando definimos nossa arquitetura baseado em Clean Architecture, organizamos os pacotes dos micro-serviços em pelo menos 3 camadas. Dessa forma, temos um pacote de Domínios, onde ficam todos os nossos objetos de domínio com suas próprias regras, um pacote de Gateways, onde ficam as interfaces, contratos e implementações de comunicações externas, e um terceiro pacote de Use Cases, que é a camada responsável por tratar todas as regras de negócio, e é o que nos interessa aqui agora.

Cada fluxo de negócio é representado por um use case, podendo também ser subdividido. Ou seja, se um fluxo de negócio possui muitas regras, ou muitos fluxos alternativos, você pode quebra-lo em casos de uso menores que também são capazes de entregar algum valor, para facilita a criação de cenários de testes.

Combinando BDD com Spock Framework

Vamos testar o primeiro cenário criado acima, que representa o fluxo principal do nosso caso de uso de alteração de senha. Faremos isso utilizando a estrutura de blocos do Spock Framework.

def "A senha do usuário deve ser alterada quando solicitado"() {
given: "um usuário logado como leonidas@sparta.gr"
def user = "leonidas@sparta.rg"
and: "deseja trocar sua senha para 9A8B12ZA"
def newPassword = "9A8B12ZA"

when: "ele solicita a troca para esta nova senha"
def useCase = new UpdatePassword()
def result = useCase.execute(user, newPassword)

then: "a senha é alterada com sucesso"
assert result.user == user
assert result.password == newPassword
}

O teste acima está validando se o comportamento do fluxo principal foi executado corretamente da forma que o cenário descreveu. O cenário é bem simples, mas se você precisar fazer mock para chamadas de banco ou qualquer outra chamada externa, o Spock também tem recursos para mocks fáceis de usar. Veja o resultado dos testes dos outros cenários aqui no projeto de exemplo.

A implementação desse caso de uso e dos resultados dos testes, você pode conferir no projeto de exemplo no github.com.

Conclusão

O BDD é uma técnica que se aplica muito bem ao desenvolvimento ágil, e que usa exemplos para descrever comportamentos de uma aplicação utilizando uma linguagem de fácil compreensão, encorajando a participação de todas as partes envolvidas.

Quando iniciamos o desenvolvimento da nossa nova plataforma de e-commerce, tanto a área de negócio, quando o time de desenvolvimento, precisavam se referir a mesma parte do sistema em desenvolvimento da mesma forma. A combinação desses elementos (BDD, Clean Architecture e Spock Framework) nos ajudou a aproximar todas as partes interessadas do projeto, tornando o desenvolvimento mais ágil e compreensível para todos os envolvidos.

Um outro detalhe muito importante e de muito valor é que o desenvolvimento dos testes não valida apenas a cobertura, mas tem um foco maior em garantir que todas as regras de negócio foram atendidas. Portanto, quanto mais específica for a responsabilidade de um caso de uso, melhor e mais fácil para testar.

Uma última observação interessante é que diferente do TDD, em que os testes devem ser escritos antes da implementação do software, com o BDD você já inicia o desenvolvimento sabendo qual vai ser o comportamento daquele fluxo, e os testes podem ser feitos antes, durante ou até depois, desde que siga cobrindo todas as regras de negócio descritas nos cenários.

--

--

Lucas Augusto
luizalabs

Arquiteto de Software — MBA em Arquitetura de Soluções — Gamer 🕹🎮 — linkedin.com/in/lucasaugustosilva