25%OFF no 1º ano | 50%OFF No 2º ano

Última chance!

Última chance!

00

DIAS

00

HORAS

00

MIN

00

SEG

Behavior Driven Development com JUnit

Behavior Driven Development com JUnit
lucas
lucas

Compartilhe

Behavior Driven Development (BDD) é uma maneira de desenvolver software que, além de outras coisas, encoraja a escrita de testes mais parecidos com uma especificação, como sugerido pelo Dan North. A ideia é que seus testes descrevam o que um determinado módulo deveria fazer ou como uma determinada funcionalidade deveria funcionar. Algo como:

Dado que eu estou na página principal, quando eu tento me logar, então meu nome aparece no canto da tela.

Eu, Cauê Guerra e Cecilia Fernandes estávamos começando um projeto aqui na Caelum e resolvemos usar BDD no desenvolvimento. Começamos, então, a procurar ferramentas que ajudam o desenvolvimento em BDD. Em Ruby existem o Cucumber e o RSpec, em Groovy tem o Easyb e, em Java, o JBehave, apenas para citar os mais famosos. Começamos a testar essas opções, mas logo no começo surgiram alguns problemas:

  • Cucumber + RSpec e Easyb: ambos eram muito parecidos em suas qualidades e defeitos: utilizam arquivos de texto para a descrição das histórias e são bem fáceis de escrever. No entanto, nenhum dos dois é feito em Java. Poderíamos usar, sem problemas, o JRuby e o próprio Groovy para acessar nosso código Java, mas por se tratar de um projeto open-source não queríamos obrigar futuros contribuidores a conhecer outras linguagens para começar a trabalhar em nosso projeto.Um outro problema foi que, usando o Eclipse como IDE, perderíamos o code completion e a habilidade de rodar os testes pela IDE - seria preciso criar alguma task ant ou um Rakefile para isso, rodando tudo pela linha de comando e tendo que interpretar os resultados obtidos fora da IDE.
  • JBehave: o JBehave adiciona um pouco de complexidade para escrever os testes (anotações, arquivos de texto não compilados, algumas restrições sobre como as classes devem ser escritas), e achamos que o ganho que teríamos ao usar o JBehave não valeria a pena por causa dessa complexidade. Além disso, teríamos problemas para reaproveitar o código dos steps, dificultando a manutenção dos testes. Existem outros problemas, mas vamos deixar isso para outro post.
Banner da prorrogação da Black Friday da Alura. Transforme a sua carreira com o maior desconto do ano, matricule-se já!

Daí nos questionamos: será que precisamos mesmo de alguma ferramenta especial para usar BDD? E a resposta que surgiu foi: Não, podemos fazer tudo usando apenas o JUnit! O bom e velho JUnit! Mas não bastava só usá-lo. Precisávamos de um jeito de organizar como uma especificação. Então pensamos e chegamos no seguinte padrão de testes:

 @Test public void nomeDoCenarioDeTestes() { given.algumContexto(); when.euExecutoAlgumaAcaoNoSistema(); then.algumaCondiçãoTemQueSerVerdadeira(); } 

Por exemplo, usando o cenário que eu tinha escrito acima: ```java @Test public void login() { given.iAmOnTheMainPage(); when.iLoginAs("Lucas"); then.myNameAppearsOnTheScreen("Lucas"); }



Um código simples, que qualquer pessoa que sabe inglês entenderia, e que nos diz o que esperamos que aconteça quando alguém se loga no sistema. Mas o que exatamente são esses "_given_", "_when_" e "_then_"? São objetos simples, que não precisam implementar nenhuma interface nem estender classe alguma - vão apenas executar o que lhes foi pedido pelo teste.

Mesmo assim resolvemos usar uma convenção para dar o nome a esses objetos, de acordo com suas responsabilidades:

- **GivenSteps given** => objeto que vai preparar o contexto inicial do teste em questão. Ex: Entrar em uma página, inserir determinados objetos no banco, logar-se com um dado usuário, etc.
- **WhenSteps when** => objeto que vai executar as ações do teste em si, utilizando o contexto definido pelo objeto _given_. É a parte mais importante do teste. Ex: Preencher um formulário, clicar no botao _Enviar_, selecionar um item em alguma comboBox, etc.
- **ThenSteps then** => objeto que verifica se o resultado das ações executadas é o esperado. Ex: O usuário está logado? Apareceu a mensagem "_Inserido com sucesso_"? Deu erro de validação?

Para que os testes ficassem o mais limpo possível e o mais parecidos com um arquivo de texto descrevendo os cenários de uma história, separamos toda a configuração de testes e a criação dos nossos objetos _given_, _when_ e _then_ em uma classe abstrata que vai ser superclasse desses testes - que representam uma **história**. Também podemos colocar a descrição da história como comentário no começo do arquivo. Vejam um exemplo completo de um Cenário de teste, com todas as classes:

```java
 /\*\* \* In order to use the system \* As a user \* I want to login \*/ public class LoginStory extends Story { @Test public void login() { given.iAmOnTheMainPage(); when.iLoginAs("Lucas"); then.myNameAppearsOnTheScreen("Lucas"); } }

public abstract class Story {

protected GivenSteps given; protected WhenSteps when; protected ThenSteps then;

@Before public void setUp() { Browser browser = createSeleniumDSL(); given = new GivenSteps(browser); when = new WhenSteps(browser); then = new ThenSteps(browser); } } public class GivenSteps {

private final Browser browser;

public GivenSteps(Browser browser) { this.browser = browser; }

public void iAmOnTheMainPage() { browser.open("index.jsp"); } }

public class WhenSteps {

private final Browser browser;

public WhenSteps(Browser browser) { this.browser = browser; }

public void iLoginAs(String login) { browser.form("login") .field("username").type(login) .field("password").type(login) .submit(); } }

public class ThenSteps {

private final Browser browser;

public ThenSteps(Browser browser) { this.browser = browser; }

public void myNameAppearsOnTheScreen(String login) { String div = browser.currentPage() .div("user").innerHTML(); Assert.assertThat(div, containsString(login)); Assert.assertThat(div, containsString("Logout")); } } 
``` O arquivo de teste mesmo fica bem limpo, apenas com o roteiro que o teste vai seguir, e o trabalho "sujo" da implementação fica nos _given_, _when_ e _then_, que podem ser reutilizados por qualquer teste. Esses objetos não precisam nem ser diferentes, não há necessidade de ter apenas um de cada na aplicação: você pode organizá-los da maneira que quiser. O importante é que no final o seu arquivo de teste fique limpo e escrito como se fosse uma especificaçao de uma funcionalidade ou a descrição de um cenário. Usando esse padrão fica fácil criar geradores de relatório, que analisam o arquivo da história e geram uma página HTML ou um arquivo de texto com as histórias que passaram e as que falharam.

Para conferir o resultado, você pode baixar o projeto que está usando essas idéias em: [http://github.com/caueguerra/calopsita](https://github.com/caelum/calopsita "https://github.com/caelum/calopsita").

Veja outros artigos sobre Inovação & Gestão