Alura > Cursos de Programação > Cursos de Java > Conteúdos de Java > Primeiras aulas do curso Boas práticas de programação: automatizando testes com Java

Boas práticas de programação: automatizando testes com Java

Testes de unidade com JUnit - Apresentação

Rodrigo: Olá! Boas-vindas ao curso de Boas Práticas de Programação com Java. Meu nome é Rodrigo Ferreira, sou um dos instrutores da Escola de Programação e vou te acompanhar ao longo desse curso.

Audiodescrição: Rodrigo se autodeclara como uma pessoa de pele branca. Tem olhos castanhos, cabelos castanhos e curtos. Está vestindo uma camiseta cinza e usando um headset branco. No ambiente em que está sentado, há uma iluminação azul. Ao fundo, uma parede com alguns quadros.

Não estou sozinho neste curso. Tenho um convidado especial!

Vinícius: Oi! Como vai? Meu nome é Vinícius de Louzada e faço parte da Escola de Programação e DevOps. Também te acompanharei nessa jornada de Boas Práticas em Programação dos Testes Automatizados com Java.

Audiodescrição: Vinícius se autodeclara como um homem de pele parda. Tem olhos castanhos escuros, barba e cabelo são pretos e curtos. Está utilizando um headset na cor branca e sentado em uma ambiente com iluminação azul.

Rodrigo, o que estudaremos durante esse curso?

Rodrigo: Vamos passar pelo conteúdo deste curso, que tem como foco os testes automatizados!

O que vamos aprender?

Aprenderemos a escrever testes automatizados com Java. Lembrando que esse curso faz parte da formação de Boas Práticas. Continuaremos com o mesmo projeto do curso anterior, cujo foco foi em Boas Práticas, melhorias, Design Patterns, Refactoring, agora focaremos em testes automatizados.

No curso anterior, fizemos várias modificações no projeto, mas em nenhum momento testamos o código. Isso foi proposital para focarmos, neste terceiro curso, em testes automatizados.

Para escrever esses testes, utilizaremos algumas ferramentas. Uma delas é o JUnit na versão 5, a principal biblioteca para Java, usada na escrita de testes automatizados.

Além do JUnit, usaremos também uma ferramenta chamada Mockito, que é uma ferramenta que nos auxilia a escrever testes utilizando mocks. Vamos aprender o que são esses mocks, quando utilizá-los e como fazer isso com o Mockito.

Também usaremos o Spring Boot, que tem um módulo focado em testes automatizados.

Este curso tem alguns requisitos, não é, Vinícius?

Vinícius: Exatamente, Rodrigo. Neste curso, precisamos que você já tenha certa intimidade com a API REST, porque aqui não vamos explicar detalhadamente o que ela é ou como realizar manutenções no código.

Também precisamos que você já tenha certo conhecimento em Spring Boot, framework com o qual trabalharemos durante este curso. Além disso, precisamos que você já tenha conhecimentos em JPA, responsável pela parte de persistência do banco de dados.

Rodrigo: São os mesmos requisitos do curso anterior. Vamos partir do mesmo projeto, que já tinha esses requisitos, e focar na parte de testes automatizados.

Então, temos muitas informações legais para aprender durante esse curso, não é, Vinícius?

Vinícius: Exatamente! Como temos bastante conteúdo para aprender, vamos começar nossa jornada!

Testes de unidade com JUnit - Testando a aplicação

Rodrigo: Vamos começar o nosso curso. O objetivo é continuarmos com o mesmo projeto que temos desenvolvido ao longo desta formação.

Anteriormente, realizamos diversas modificações e melhorias em nosso código. No entanto, existe um ponto interessante a ser destacado: no curso anterior, trabalhamos intensamente na melhoria do projeto Adopet API, realizando várias atualizações no código. Este estava bastante confuso e não seguia nenhuma das boas práticas de programação.

Ao longo do curso, fizemos as refatorações necessárias, aplicamos padrões de projeto e utilizamos uma melhor orientação a objetos. O resultado foi um código bem organizado, fácil de entender e de manter.

Contudo, de propósito, em nenhum momento do curso anterior realizamos testes na aplicação. Por mais que o código esteja funcionando e bem estruturado, se não é possível comprovar que ele continua funcionando após as mudanças, todas as melhorias são em vão.

Vinícius: Exatamente por esse motivo que precisamos testar nossa aplicação. Até mesmo para garantir que as melhorias não causaram nenhum problema no código ou geraram algum bug. Precisamos realizar alguns testes.

Antes de começarmos com os testes, vale a pena relembrar que, nos cursos anteriores, tínhamos uma aplicação console a partir da qual disparávamos algumas requisições via comandos, como solicitar adoção, listar pets. Quando essas requisições eram disparadas através do console, elas eram enviadas à API.

Agora, com o IntelliJ aberto, é possível visualizar as modificações feitas na API. Observe que no canto esquerdo temos um diretório chamado "src". Ao expandi-lo, podemos acessar o diretório "main" e em seguida o diretório "java".

Dentro do diretório "java", há um pacote chamado br.com.alura.adopet.api. Nele, há algumas pastas que correspondem às partes do projeto onde as refatorações foram realizadas.

Temos as classes controller, dto, exception, model, repository. Se clicarmos na pasta controller e depois na classe AdocaoController, encontraremos um método chamado solicitar(). Atualmente ele está bem enxuto, no curso anterior ele continha muitas regras de negócio e era bastante confuso.

Porém, retornando à nossa problemática atual, precisamos testar. Afinal, como nós saberemos se não ocorreu nenhum problema no código? Precisamos garantir com os testes, não é mesmo?

Rodrigo: Nossa ideia é focar justamente na questão de testes. Como se trata de uma API, podemos usar ferramentas como Postman, Insomnia entre outras para realizá-los.

Vinícius: Para testar a API, precisamos iniciá-la. No canto superior direito do IntelliJ temos um botão de "Play". Ao clicar nele, podemos ver a opção "Run AdopetAPIApplication". Vamos selecioná-la para subir a API. Ao iniciarmos a API, poderemos realizar disparos a ela.

Ressaltando que, como não temos a aplicação console, utilizaremos outro programa como o Postman, para realizar essas requisições. A intenção é verificar se não ocorreu nenhum erro no código, como, por exemplo, se está retornando a lista de pets, de tutores. Próximo passo é abrir o Postman e começarmos os teste.

Já estamos com o Postman aberto! Nós disponibilizaremos uma atividade com passo a passo de instalação. Estou conectado à minha conta e sugiro que você também faça iss para que seja possível realizar alguns procedimentos no decorrer da nossa jornada.

Rodrigo, qual é a ideia do Postman? Falamos que vamos realizar disparos para a API e poderíamos criar requisições manualmente, não é mesmo?

Rodrigo: Perfeito! O Postman é uma ferramenta para testar APIs e precisamos criar as requisições, definir a URL da API, os endereços que vamos chamar, enviar os JSONs (JavaScript Object Notation) e muito mais.

Contudo, para facilitar sua vida, já temos um arquivo que está no projeto. Se trata de um arquivo JSON que podemos importar no Postman e ele conterá todas as requisições preparadas.

O Vini ensinará como importar esse arquivo no Postman e, com isso, poderemos visualizar todas as requisições e efetuar os disparos pela interface dessa ferramenta.

Vinícius: Então, vamos importar nosso arquivo JSON!

No canto superior esquerdo, temos um botão chamado "Import". Ao selecioná-lo, encontraremos duas opções: soltar o arquivo ou clicar em Files para abrir uma janela para o diretório de arquivos do computador. Então, navegamos até a pasta do projeto.

No meu caso, está na área de trabalho, na pasta "Boas Práticas de Java". Dentro dela, há um arquivo chamado Adopet.postman_collection.json.

Lembrando que esse arquivo acompanha o projeto inicial que você baixou no curso. Por isso, não deixe de baixar o projeto inicial.

Dando um duplo clique no arquivo, ele começará a carregar. No canto superior esquerdo, é possível conferir que uma pasta chamada "Adopet" foi importada.

Abrindo a pasta, encontraremos outras pastas com os nomes : abrigo; adoção; pet; e tutor. Essas pastas são compostas por várias requisições. Em "abrigo", por exemplo, temos as requisições de "listar abrigos", "cadastrar abrigos", "listar pets do abrigo" e "cadastrar pet do abrigo".

Rodrigo, temos várias requisições. Qual você recomenda escolhermos para testarmos nossa aplicação?

Rodrigo: Perfeito! Todas as requisições estão disponíveis neste arquivo. Podemos testar pelo "abrigo" mesmo, já que você expandiu. Clique, por exemplo, em "listar abrigos".

Tem a requisição, tem os parâmetros, está tudo pronto e podemos disparar e simular a chamada na nossa API.

Vinícius: Existe um botão do lado direito do nosso visor escrito "Send". Vamos enviar uma requisição do tipo "Get" e queremos recuperar todos os abrigos.

Clicaremos no botão "Send", que é azul. Em seguida, na parte de baixo, teremos um retorno e conseguiremos visualizar todos os abrigos que estão registrados em nossa aplicação.

O abrigo 1 é o "Abrigo feliz", o abrigo 2 é o "Abrigo xpto" e o abrigo 3 é o "Abrigo xptv". Em nenhum caso houve uma quebra no código, não é mesmo, Rodrigo?

Rodrigo: Sim, enviamos a requisição, que é para listar abrigos. Ela chamou a API, que foi até o banco de dados. No caso do Vini, no computador dele, o MySQL já tem esses registros cadastrados. A API está retornando corretamente o JSON com a lista de todos os abrigos registrados. A princípio, tudo está funcionando corretamente na requisição de listar abrigos.

Vinícius: Imagine um cenário em que Rodrigo fosse criar uma nova funcionalidade para essa nossa aplicação. Teríamos que executar todos esses testes manualmente, o que seria extremamente cansativo e nada prático. É por isso que, neste curso, aprenderemos a realizar testes automatizados. Até mais!!

Testes de unidade com JUnit - Conhecendo o JUnit

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 >= 8) {
           return ProbabilidadeAdocao.ALTA;
       }
             
       if (nota >= 5) {
           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).

// Código omitido. 

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.

// Código omitido. 

 private int calcularNota(Pet pet) {
        int peso = pet.getPeso().intValue();
        int idade = pet.getIdade();
        TipoPet tipo = pet.getTipo();

        int nota = 10;

        // penalizando pelo peso muito alto
        if (tipo == TipoPet.CACHORRO && peso > 15) {
            nota -= 2;
        }
        if (tipo == TipoPet.GATO && peso > 10) {
            nota -= 2;
        }

        // penalizando pela idade avançada
        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.

// Código omitido. 

  <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 {

    @Test
    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!

Sobre o curso Boas práticas de programação: automatizando testes com Java

O curso Boas práticas de programação: automatizando testes com Java possui 180 minutos de vídeos, em um total de 55 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Java acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas