Todo desenvolvedor de software em algum momento já escreveu um trecho de código que não funcionava. Esse débito técnico, chamado de forma genérica de bug só é descoberto quando esse código é testado.
Entretanto testar um código pode ser uma tarefa complexa:
- Ambientes de desenvolvimentos complexos (na minha máquina funciona);
- Quantidades de testes a serem executados;
- Complexidade dos teste (uso de bancos de dados, sistemas de terceiros ou algum equipamento);
- Outros.
Como forma de suportar essa tarefa, diversas tecnologias surgiram, tais como o Framework Spock, Junit. Além disso surgiram abordagens distintas para uso de testes, tais como o Test Driven Develpment (TDD).
Para exemplificar desenvolveremos um sistema de leilão. Nesse sistema, um determinado trecho de código é responsável por devolver o maior lance de um leilão. Veja a implementação deste código:
public class Avaliador {
private Double maiorDeTodos = Double.NEGATIVE_INFINITY;
public void avalia(Leilao leilao) {
for (Lance lance : leilao.getLances()) {
if (lance.getValor() > maiorDeTodos) {
maiorDeTodos = lance.getValor();
}
}
}
public double getMaiorLance() {
return maiorDeTodos;
}
}
E para testar o código, criaremos uma classe TestarAvaliador
:
public class TestarAvaliador {
public static void main(String[] args) {
Usuario joao = new Usuario("Joao");
Usuario jose = new Usuario("José");
Usuario maria = new Usuario("Maria");
Leilao leilao = new Leilao("Playstation 3 Novo");
leilao.propoe(new Lance(jose, 400.0));
leilao.propoe (new Lance (joao, 300.0));
leilao.propoe (new Lance(maria, 200.0));
Avaliador leiloeiro = new Avaliador();
leiloeiro.avalia(leilao);
// imprime 400.0
System.out.println(leiloeiro.getMaiorLance());
}
}
O código funciona! Ao receber um leilão, a lista de lances é percorrida, buscando o maior valor. Veja que a variável maiorDeTodos
é inicializada com o menor valor que cabe em um Double
. Isso faz sentido, já que queremos que, na primeira vez que o for seja executado, ele caia no if
e substitua o menor valor do Double
pelo primeiro valor da lista de lances.
No sistema proposto, também será implementada a busca pelo menor lance de todos. Faz sentido que esta regra de negócio esteja também na classe Avaliador
. Basta acrescentarmos mais uma condição, desta vez para calcular o menor valor:
public class Avaliador {
private Double maiorDeTodos = Double.NEGATIVE_INFINITY;
private Double menorDeTodos = Double.POSITIVE_INFINITY;
public void avalia(Leilao leilao) {
for (Lance lance : leilao.getLances()) {
if (lance.getValor() > maiorDeTodos) {
maiorDeTodos = lance.getValor();
}
else if(lance.getValor() < menorDeTodos) {
menorDeTodos = lance.getValor();
}
}
}
public double getMaiorLance() {
return maiorDeTodos;
}
public double getMenorLance() {
return menorDeTodos;
}
}
E para testar, vamos programar também a saída para o menor valor:
public class TestarAvaliador {
public static void main(String[] args) {
Usuario joao = new Usuario("Joao");
Usuario jose = new Usuario("José");
Usuario maria = new Usuario("Maria");
Leilao leilao = new Leilao("Playstation 3 Novo");
leilao.propoe(new Lance(jose, 400.0));
leilao.propoe (new Lance (joao, 300.0));
leilao.propoe (new Lance(maria, 200.0));
Avaliador leiloeiro = new Avaliador();
leiloeiro.avalia(leilao);
// imprime 400.0
System.out.println(leiloeiro.getMaiorLance());
// imprime 250.0
System.out.println(leiloeiro.getMenorLance());
}
}
Tudo parece estar funcionando. Apareceram na tela tanto o maior, quanto o menor valor. Vamos colocar o sistema em produção, afinal está testado! Mas será que o código está realmente correto? Veja agora um outro teste, muito parecido com o anterior, agora com os lances organizados em ordem crescente:
public class TestarAvaliador {
public static void main(String[] args) {
Usuario joao = new Usuario("Joao");
Usuario jose = new Usuario("José");
Usuario maria = new Usuario("Maria");
Leilao leilao = new Leilao("Playstation 3 Novo");
leilao.propoe(new Lance(maria, 200.0));
leilao.propoe(new Lance(joao, 300.0));
leilao.propoe(new Lance(jose, 400.0));
Avaliador leiloeiro = new Avaliador();
leiloeiro.avalia(leilao);
// imprime 400.0
System.out.println(leiloeiro.getMaiorLance());
// imprime 250.0
System.out.println(leiloeiro.getMenorLance());
}
}
Veja que, para um cenário um pouco diferente, nosso código não funciona! O que não funciona? Outra pergunta importante: no mundo real, como descobrir esse bug facilmente?
Será que a melhor solução é esperar o cliente ligar para a empresa (#chateado) porque o sistema não funciona? Infelizmente, bugs em software são uma coisa mais comum do que deveriam ser. Bugs nos fazem perder a confiança do cliente, e nos custam muito dinheiro. Afinal, precisamos corrigir o bug e recuperar o tempo perdido do cliente, que ficou parado, enquanto o sistema não funcionava.
Por que nossos sistemas apresentam tantos bugs assim? Um dos motivos para isso é a falta de testes, ou seja, testamos muito pouco! Equipes de software geralmente não gostam de fazer (ou não fazem) os devidos testes. As razões para isso são geralmente a demora e o alto custo para testar o software como um todo. E faz todo o sentido: pedir para um ser humano testar todo o sistema é impossível: ele vai levar muito tempo para isso!
Como resolver esse problema? Fazendo a máquina testar! Escrevendo um programa que teste nosso programa de forma automática! Uma máquina, com certeza, executaria o teste muito mais rápido do que uma pessoa! Ela também não ficaria cansada ou cometeria erros!
Um teste automatizado é muito parecido com um teste manual. Imag- ine que você está testando manualmente uma funcionalidade de cadastro de produtos em uma aplicação web. Você, com certeza, executará três passos diferentes. Em primeiro lugar, você pensaria em um cenário para testar. Por exemplo, “vamos ver o que acontece com o cadastro de funcionários quando eu não preencho o campo de CPF”, Após montar o cenário, você executará a ação que quer testar. Por exemplo, clicar no botão “Cadastrar”. Por fim, você olharia para a tela e verificaria se o sistema se comportou da maneira que vocé esperava. Nesse nosso caso, por exemplo, esperaríamos uma mensagem de erro como “CPF inválido”.
Um teste automatizado é muito parecido. Você sempre executa estes três passos: monta o cenário, executa a ação e valida a saída. Mas, acredite ou não, já escrevemos algo muito parecido com um teste automatizado neste capítulo. Lembra da nossa classe TesteDoAvaliador? Perceba que ela se parece com um teste, afinal ela monta um cenário e executa uma ação. Veja o código:
E veja que ele é automatizado! Afinal, é a máquina que monta o cenário e executa a ação (nós escrevemos o código, sim, mas na hora de executar, é a máquina que executa!). Não gastamos tempo nenhum para executar esse teste. O problema é que ele ainda não é inteiro automatizado. A parte final (a validação) ainda é manual: o programador precisa ver o que o programa imprimiu na tela e checar se o resultado bate com o esperado.
Para melhorar isso, precisamos fazer a própria máquina verificar o resul- tado. Para isso, ela precisa saber qual a saída esperada. Ou seja, a máquina deve saber que o maior esperado, para esse caso, é 400, e que o menor es- perado é 250. Vamos colocá-la em uma variável e entáo pedir pro programa verificar se a saída é correta:
Ao rodar esse programa, a saída já é um pouco melhor:
true false
Veja que agora ela sabe o resultado esperado e verifica se a saída bate com ele, imprimindo true se o resultado bateu e false caso contrário. Estamos chegando perto. O desenvolvedor ainda precisa olhar todos os trues
e falses
e ver se algum deu errado. Imagina quando tivermos 1000 testes iguais a esses? Será bem complicado!
Precisávamos de uma maneira mais simples e elegante de verificar o resultado de nosso teste. Melhor ainda se soubéssemos exatamente quais testes falharam e o porquê. Para resolver esse problema, podemos utilizaremos o JUnit, o framework de testes de unidade mais popular do mundo Java. O JUnit é uma ferramenta bem simples. Tudo que ele faz é ajudar na automatização da última parte de um teste, a validação, e a exibir os resultados de uma maneira bem formatada e simples de ser lida.