Rodrigo: Já realizamos os testes usando a ferramenta Postman, que é uma ferramenta útil para testar uma API. No entanto, tais testes são bastante manuais.
Apesar de já termos preparado as requisições JSON para este curso, em um projeto real, seria necessário escrever todo esse código e criar cada uma das requisições.
Isso pode consumir bastante tempo, principalmente nas fases futuras do projeto, quando houver atualizações e modificações. Embora seja possível automatizar alguns pontos com o Postman, o objetivo deste curso é em testes automatizados ao nível de código.
Vamos aprender a escrever códigos para testar as classes que criamos no projeto, garantindo que estejam funcionando corretamente e detectando qualquer bug que possa surgir. Esse é o esquema de teste automatizado em Java.
Vinícius: Perfeito! Vamos voltar para a IDE IntelliJ. Se analisarmos a estrutura do projeto, vamos verificar que há muitos arquivos e pode surgir a questão "O que vamos testar?".
É ideal testar as áreas do nosso código que contêm regras de negócio. Portanto, vamos abrir as pastas "src > main > java", que contêm o pacote da br.com.alura.adopet.api. Nele, há várias classes e pacotes, incluindo controller, DTO, exception, model, service e validacoes.
Temos várias classes de service e de validacao. Por onde podemos começar?
Rodrigo: Queremos escrever testes automatizados e a ideia é testarmos as classes do projeto, garantir que tudo está funcionando. Mas, em um projeto, não temos em torno de 5 classes, mas dezenas ou até mesmo centenas de classes.
Você mencionou algo importante: um bom ponto de partida seria procurar classes que têm validações, regras de negócio, cálculos e algoritmos. Essas são as áreas críticas do sistema, onde geralmente ocorrem erros de digitação ao escrevermos os algoritmos.
Portanto, os pacotes service e validacao são bons candidatos, pois provavelmente é onde estarão as classes com regras de negócio da aplicação. Podemos escolher algumas delas para entendermos o que elas fazem e como podemos testá-las de forma automatizada com Java.
Por exemplo, no pacote service, existe uma classe chamada CalculadoraProbabilidadeAdocao, que é uma das classes importantes do projeto. Esta classe tem um algoritmo que calcula a probabilidade de uma pessoa adotar um determinado pet, levando em conta suas características.
public class CalculadoraProbabilidadeAdocao {
public ProbabilidadeAdocao calcular(Pet pet) {
int nota = calcularNota(pet);
if (nota >&
return ProbabilidadeAdocao.ALTA;
}
if (nota >&
return Probabilidade Adocao.MEDIA;
}
return Probabilidade Adocao.BAIXA;
}
// Código omitido.
Essa é uma ótima classe para testarmos! Será que esse código, com tantos ifs, está funcionando conforme o esperado? Será que há algum bug?
Vinícius: Perfeito, já abrimos a classe CalculadoraProbabilidadeAdocao. O nome é bastante descritivo. Essa classe possui um método responsável por calcular a probabilidade de adoção de determinado pet.
Suponhamos que temos um gato que possui um nome, idade e peso. Com base nesses atributos, nossa classe avaliará a probabilidade de adoção desse pet. A probabilidade pode ser alta, média ou baixa, dependendo desses parâmetros. Vamos entender melhor essa nossa classe.
A classe possui um método calcular(), que espera receber um pet e retorna uma probabilidade de adoção (alta, média ou baixa). Dentro desse método, existe uma chamada para um método privado que calcula a nota desse pet: calcularNota(pet).
public Probabilidade Adocao calcular(Pet pet) {
int nota = calcularNota(pet);
if (nota >= 8) {
return ProbabilidadeAdocao.ALTA;
}
if (nota >= 5) {
return Probabilidade Adocao.MEDIA;
}
return Probabilidade Adocao.BAIXA;
}
// Código omitido.
O código vai analisar o peso, idade e tipo do pet, seja ele cachorro ou gato. Todo pet começa com uma nota 10 e, conforme algumas características, nosso algoritmo analisa e penaliza essa nota.
private int calcularNota(Pet pet) {
int peso = pet.getPeso().intValue();
int idade = pet.getIdade();
TipoPet tipo = pet.getTipo();
int nota = 10;
if (tipo == TipoPet.CACHORRO && peso > 15) {
nota -= 2;
}
if (tipo == TipoPet.GATO && peso > 10) {
nota -= 2;
}
if (idade >= 15) {
nota -= 5;
}else if (idade >= 10) {
nota -= 4;
}
return nota;
}
}
Por exemplo, temos penalidades por peso muito alto. Se for um cachorro e o peso for maior que 15 quilos, ele perde dois pontos. Se for um gato e o peso for maior que 10, ele também perde dois pontos.
Outra validação aqui é a penalização pela idade avançada. Caso o cachorro tenha uma idade maior que 15, ele perde cinco pontos. Após esses cálculos, o método retorna uma nota.
Com essa nota, temos uma estrutura para analisar a nota. Se for maior ou igual a 8, retorna uma probabilidade alta. Se a nota for maior ou igual a 5, retorna a média. Se a nota for menor que isso, a probabilidade será baixa.
Portanto, Rodrigo, nós podemos começar por realizar um teste automatizado para essa classe, certo?
Rodrigo: Certamente! É interessante notar que, mesmo não sendo um código muito complexo ou extenso, temos um algoritmo que pega um objeto, checa atributos, faz cálculos e há possibilidade de haver bugs.
Pode ser que a pessoa desenvolvedora que escreveu esse código tenha cometido um erro, por exemplo, colocou uma condição errada, um link errado, um sinal de "menor" quando deveria ser "maior" ou "menor ou igual".
A maneira de testar isso poderia ser disparando várias requisições, simulando cada um dos pets no Postman, o que seria mais trabalhoso, seria um teste manual. Teríamos que testar cada possibilidade.
Ou podemos escrever um teste automatizado, que seria um código para testar o nosso código, para validar e garantir que ele está funcionando de maneira muito mais rápida e prática.
Neste ponto, podem surgir algumas dúvidas: Como fazer isso? Como escrever um teste automatizado para essa classe e esse método?
Vinícius: Podemos usar bibliotecas já prontas para realizar testes automatizados, como, por exemplo, JUnit.
JUnit é uma biblioteca padrão do Java que serve para realizar esse tipo de teste. Ou seja, escrevemos um teste, avaliamos o que esperamos receber e validamos se o código está certo ou não.
Porém, deve surgir outra pergunta: o que é necessário para usar JUnit? Precisamos baixá-lo? Instalá-lo?
Rodrigo: Boa pergunta! Queremos usar uma nova biblioteca no nosso projeto, e esse projeto está utilizando o Maven, então precisamos abrir o arquivo pom.xml, que está no diretório raiz da aplicação.
Toda aplicação Maven, de arquivo pom.xml, tem uma sessão de dependências.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
// Código omitido.
Neste caso, o projeto utiliza o Spring Boot, então tem as dependências do Spring, do MySQL, e de todas as outras bibliotecas e frameworks que estamos utilizando no projeto.
Há também uma última dependência, a Spring-boot-starter-test. Essa é uma dependência do Spring que já inclui várias outras bibliotecas e dependências para testes.
// Código omitido.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
// Código omitido.
Por isso, não precisaremos alterar nada no projeto, pois já temos essa dependência do Spring. Ela já inclui o JUnit e outras bibliotecas para testes.
Vinícius: Vamos voltar agora para nossa calculadora de probabilidade de adoção.
Para criarmos uma classe de teste, podemos clicar diretamente no nome da classe, que nesse caso, é a CalculadoraProbabilidadeAdocao. Utilizando o atalho "Alt + Insert", ele abrirá uma janela onde teremos a opção de adicionar um novo teste.
Ao selecionarmos essa opção, o programa perguntará qual biblioteca gostaríamos de utilizar. Nessa situação, utilizaremos o JUnit 5.
Ao criarmos essa nova classe, ela receberá o mesmo nome da classe original, mas com o acréscimo da palavra "Teste" no final: CalculadoraProbabilidadeAdocaoTest. Por ora, não nos preocuparemos com as demais opções, apenas clicaremos em "Ok".
Agora, o IntelliJ criou uma nova classe de teste. Se abrirmos o diretório de arquivos do IntelliJ, encontraremos uma pasta no "src" chamada "Test". Nela, encontramos o diretório "java". Então, localizaremos o pacote "AdopetApi" e a pasta "api.service", onde está localizada a nova classe de teste para a CalculadoraProbabilidadeAdocao.
Ao trabalharmos com a calculadora de probabilidade de adoção, temos vários cenários para testar. Por exemplo, o cenário de um cão com peso muito alto e idade baixa. Precisamos criar esses cenários para testar. Então, vamos criar um primeiro método chamado void cenario01().
Para sinalizarmos que esse método é um cenário de teste, ou seja, algo que queremos testar com testes automatizados, precisamos adicionar uma anotação antes do método. Usaremos @Test, desse modo o IntelliJ nos ajuda, perguntando se queremos importar da biblioteca JUnit Jupiter API. Ao confirmarmos, ele realizará o import desta biblioteca.
package br.com.alura.adopet.api.service;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class Calculadora ProbabilidadeAdocaoTest {
&
void cenario01(){
}
}
Pronto, agora podemos escrever esse cenário de teste.
Rodrigo: Para criar um teste automatizado, criamos uma classe de teste, que é separada da classe original. Ela é armazenada no diretório " src > teste > java", no mesmo pacote da classe que estamos testando.
Dentro dessa classe de teste, criamos métodos para simular os cenários. Porém, essa classe não tem um método main, ela não roda como tal. Para sinalizarmos para o JUnit, que é quem rodará a classe, usamos a anotação @Test.
Para cada possibilidade, ou seja, cada cenário, criamos um método separado. Aprenderemos como escrever esse tipo de código na sequência!