Vamos apresentar aqui um exemplo de teste automatizado utilizando o
JUnit, com base na abordagem TDD (Test Driven Development). De acordo
com Santos Filho e Oliveira (2014), o TDD segue duas regras simples: a
primeira é que um novo teste só deve ser escrito se um teste
automatizado falhar, e a segunda é que deve-se buscar eliminar
duplicidade. Apesar de simples, essas regras geram um complexo
comportamento expresso pelo ciclo red-green-refactor (vermelho-verde-refatora), que é baseado em:
- Vermelho: escrever um teste que não funciona;
- Verde: fazer o teste passar, escrevendo uma pequena mudança no código, mesmo que não seja a mais adequada;
- Refatora: melhorar o código, eliminando todas as duplicidades e mantendo-o verde.
“‘…TDD é simplesmente você escrever o teste do seu programa antes de
codificá-lo de fato. Teste nesse contexto seriam os testes unitários.’
Ao meu ver a essência está correta, porém talvez faltasse deixar claro que TDD não se trata apenas de escrever testes antes do código.
Esta é a primeira visão que a maioria dos iniciantes têm sobre
desenvolvimento orientado a testes: que basta escrever todos os testes
antes e pronto. Isto é compreensível, já que escrever testes antes do
código já causa um choque por si só. Porém, TDD foi definido pelo Kent
Beck com a seguinte fórmula:
TDD = Test-First + Design Incremental
E a parte do design incremental ficou um pouco confusa na continuação do texto:
‘…O processo de desenvolvimento usando TDD é muito simples:
1 - Escolha a classe e o método que você que codificar e pense em todos os testes possíveis para ela;
2- Antes de escrever a classe, codifique os testes que você pensou…’
Seguindo estas instruções (escrever todos os testes possíveis
antes) pode-se perder um pouco de um dos maiores benefícios de TDD, que é
prover feedback rápido sobre o que está sendo implementado. Aí que entra o design incremental. A ideia é que a solução seja criada em pequenos passos (Baby Steps), seguindo a rotina descrita no livro TDD by Example:
1 - Escreva um pequeno teste que falhe, e talvez até mesmo sequer compile inicialmente;
2 - Faça o teste passar da maneira rápida, cometendo qualquer ‘pecado’ durante este processo;
3 - Refatore: elimine toda duplicação criada para fazer os testes passarem.
Desta forma, não é preciso pensar na solução completa (sequer
numa classe inteira) antes de começar, basta pensar em um teste apenas e
fazê-lo passar. Com o conhecimento adquirido é possível então passar
para o próximo teste com mais segurança, e assim avançar até que a
solução esteja completa.
Outra noção comum é pensar que
TDD trata apenas de testes de unidade.
DD faz bastante uso de testes unitários. Porém, embora a maioria dos
exemplos mostra TDD sendo feito através deste tipo de teste, isto não
significa que a técnica está restrita a eles. Escrever testes de
aceitação simples antes do código existir também pode colaborar na hora
de desenvolver com TDD, já que com eles também pode-se obter feedback sobre a solução.
Embora sejam bem pontuais, observar estes detalhes pode ajudar no uso mais eficiente de TDD.”
Para iniciar o planejamento dos testes, temos que partir de um problema
específico e usaremos um caso clássico denominado problema FizzBuzz. O
FizzBuzz consiste em exibir uma lista de 1 a 30, um em cada linha, e
filtrar todos os números respeitando as regras:
|
Retorno do Fizz Buzz
|
números divisíveis por 3 devem retornar “Fizz”
números divisíveis por 5 devem retornar “Buzz”
números divisíveis por 3 e 5 devem retornar “FizzBuzz”
Fonte: Elaborado pela autora
Antes
de começar a escrever os testes, é preciso definir o que precisa ser
testado, para isso deve-se criar uma lista com todos os testes que sejam
necessários. No nosso utilizaremos a seguinte lista para os testes:
[1] Retornar 1 ao passar 1;
[2] Retornar 2 ao passar 2;
[3] Retornar Fizz ao passar 3;
[4] Retornar 4 ao passar 4;
[5] Retornar Buzz ao passar 5;
[6] Retornar Fizz ao passar 6;
[7] Retornar 7 ao passar 7;
[8] Retornar 8 ao passar 8;
[9] Retornar Fizz ao passar 9;
[10] Retornar Buzz ao passar 10;
[11] Retornar FizzBuzz ao passar 15;
[12] Retornar FizzBuzz ao passar 30.
O teste [1] da lista deve ser implementado e retornar 1 quando passado 1 como parâmetro. O método que executa este teste é:
@Test
public void retornaUmParaUm(){
Fizzbuzz fizzbuzz = new Fizzbuzz();
assertEquals("1", fizzbuzz.verificaFizzbuzz(1));
}
Ao executar o teste é apresentado o seguinte resultado:
Nenhum teste passou, 1 teste causou 1 erro (0,99 s)
retornaUmParaUm causou um Erro: Uncompilable source code – Erroneous tree type: <any>
Na primeira tentativa, o teste não vai
passar porque não existe a classe Fizzbuzz nem o método
verificaFizzbuzz(). Assim, o próximo passo é fazer o teste passar com o
mínimo de código. Para isto, deve ser implementada a classe Fizzbuzz e o
método verificaFizzbuzz retornando 1, atendendo ao teste [1] da lista.
public class Fizzbuzz {
public String verificaFizzbuzz(Integer numero){
return "1";
}
}
Como resultado da execução do teste [1], obtém-se a seguinte saída:
Agora é necessário implementar o teste [2] da lista, que retorna 2 quando passado 2 como parâmetro.
@Test
public void retornaDoisParaDois(){
Fizzbuzz fizzbuzz = new Fizzbuzz();
assertEquals("2", fizzbuzz.verificaFizzbuzz(2));
}
Ao executar esse teste, é apresentado um
erro, pois o método verificaFizzbuzz vai retornar sempre 1. Executando o
teste, é apresentado o seguinte resultado:
1 teste passou, 1 teste falhou. (0,123 s)
retornaDoisParaDois Falhou: expected <[2]> but was
<[1]>
É necessário refatorar o código para fazê-lo passar. As mudanças apresentadas são:
public String verificaFizzbuzz(Integer numero){
return numero.toString();
}
Com essas mudanças, o teste [2] passou. Como resultado da execução foi apresentada a seguinte saída:
Ambos os testes passaram. (0,108 s)
Nenhum comentário:
Postar um comentário