Explorando Testes Unitários com JUnit 5 e Mockito: Um Exemplo Prático
Tags: java, springboot, testing, tutorial
Published: 2023-09-07 16:15:56

Testar software é uma etapa crucial no desenvolvimento de aplicações. Testes unitários garantem que partes individuais do código funcionem conforme o esperado. Neste artigo, vamos explorar um exemplo de código que utiliza o framework de testes JUnit 5 e a biblioteca Mockito para testar uma classe chamada PlanetServices.

### Entendendo o Código de Teste

O código de teste em questão é responsável por testar os métodos da classe PlanetServices. Essa classe oferece serviços relacionados a planetas,  um crud básico: criar, buscar, listar planetas, deletar.

### Código fonte da aplicação: 

> https://github.com/diegoSbrandao/EstudoTestesAutomatizados/blob/main/starwars/src/test/java/br/com/sw/api/domain/PlanetServicesTest.java

Antes da criação dos testes, crie um pacote a parte para criarmos uma classe com duas constantes para usarmos nos testes, exemplo abaixo:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65er442w58vg9saw3xd8.png) 

O teste começa importando várias classes e métodos estáticos de bibliotecas, como org.assertj.core.api.Assertions e org.mockito.Mockito. Essas importações são usadas para fazer asserções nos testes e configurar comportamentos simulados.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/32rg58mdurrzw3ni131t.png)


A anotação @ExtendWith(MockitoExtension.class) na classe de teste indica que a extensão Mockito será usada para fornecer suporte a objetos simulados (mocks).

A anotação @InjectMocks é usada para injetar automaticamente os objetos simulados criados com @Mock na classe de teste. No exemplo, a classe PlanetServices será injetada com o mock PlanetRepository.





## Testando a Criação de Planetas

Vamos analisar o primeiro teste:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/foz3rmkvqvu2t506dqtl.png)

1. A configuração when(planetRepository.save(PLANET)).thenReturn(PLANET) diz que, quando o método save do mock planetRepository for chamado com o argumento PLANET, ele deve retornar o próprio PLANET.

2.	A chamada planetServices.create(PLANET) invoca o método create da classe PlanetServices com o objeto PLANET, e o resultado é atribuído à variável sut.

3.	A asserção assertThat(sut).isEqualTo(PLANET) verifica se o objeto retornado é igual ao objeto esperado PLANET.





## Testando a Criação de Planetas com Dados Inválidos

O próximo teste aborda um cenário em que a criação de um planeta com dados inválidos leva a uma exceção:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02flm3uo33kyw5uy5ql0.png)

1. A configuração when(planetRepository.save(INVALID_PLANET)).thenThrow(RuntimeException.class) instrui o mock planetRepository a lançar uma exceção do tipo RuntimeException quando o método save for chamado com INVALID_PLANET.

2.  A expressão assertThatThrownBy(() -> planetServices.create(INVALID_PLANET)).isInstanceOf(RuntimeException.class) verifica se a chamada ao método create com INVALID_PLANET lança uma exceção do tipo esperado





## Buscando um Planeta por ID Existente

O próximo teste lida com a busca de um planeta por um ID existente:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5brlcpfd8rcm75fqh5m.png)

1. A configuração when(planetRepository.findById(1L)).thenReturn(Optional.of(PLANET)) diz que, quando o método findById do mock planetRepository for chamado com o argumento 1L, ele deve retornar um Optional contendo o objeto PLANET.

2. A chamada planetServices.findById(1L) invoca o método findById da classe PlanetServices com o ID 1L. O resultado é atribuído a sut, um Optional de Planet.

3. As asserções verificam se o Optional não está vazio e se o objeto contido nele é igual a PLANET.





## Buscando um Planeta por ID Inexistente

O próximo teste testa a busca de um planeta por um ID que não existe:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qve1qxzpzryny4ww66b3.png)

1. A configuração when(planetRepository.findById(1L)).thenReturn(Optional.empty()) diz que, quando o método findById do mock planetRepository for chamado com o argumento 1L, ele deve retornar um Optional vazio.

2. A chamada planetServices.findById(1L) invoca o método findById da classe PlanetServices com o ID 1L. O resultado é atribuído a sut, um Optional de Planet.

3. A asserção verifica se o Optional está vazio.





## Buscando um Planeta por Nome Existente

O teste a seguir lida com a busca de um planeta por um nome existente.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s96kulxgqr5dp88pcpg8.png)

1. A configuração when(planetRepository.findByName("Tatooine")).thenReturn(Optional.of(PLANET)) instrui o mock planetRepository a retornar um Optional contendo o objeto PLANET quando o método findByName for chamado com o argumento "Tatooine".

2. A chamada planetServices.findByName("Tatooine") invoca o método findByName da classe PlanetServices com o nome "Tatooine". O resultado é atribuído a sut, um Optional de Planet.

3. As asserções verificam se o Optional não está vazio e se o objeto contido nele é igual a PLANET.





## Buscando um Planeta por Nome Inexistente

Este teste verifica a busca de um planeta por um nome que não existe:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/we6m1p9e9xanb8183qxl.png)

1. A configuração when(planetRepository.findByName(name)).thenReturn(Optional.empty()) instrui o mock planetRepository a retornar um Optional vazio quando o método findByName for chamado com o argumento name.

2. A chamada planetServices.findByName(name) invoca o método findByName da classe PlanetServices com o nome especificado. O resultado é atribuído a sut, um Optional de Planet.

3. A asserção verifica se o Optional está vazio.




## Listando Planetas

Os testes a seguir abordam a listagem de planetas com base em diferentes cenários.

## Testando a listagem de todos os planetas:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lwns75ox8i0xtkmwy4k.png)

1. Uma lista planets é criada com um planeta PLANET adicionado a ela.

2. É criado um exemplo de consulta query usando a classe QueryBuilder com base nas propriedades do objeto PLANET.

3. A configuração 
when(planetRepository.findAll(query)).thenReturn(planets) instrui o mock planetRepository a retornar a lista de planetas quando o método findAll for chamado com a query especificada.

4. A chamada planetServices.list(PLANET.getTerrain(), PLANET.getClimate()) invoca o método list da classe PlanetServices com os parâmetros de terreno e clima do planeta.

5. As asserções verificam se a lista retornada não está vazia, se possui um único elemento e se esse elemento é igual ao objeto PLANET.





## Testando a listagem vazia de planetas:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsyz644jc4s2hezzvfxa.png)


1. A configuração when(planetRepository.findAll((Example<Planet>) any())).thenReturn(Collections.emptyList()) instrui o mock planetRepository a retornar uma lista vazia quando o método findAll for chamado com qualquer argumento.

2. A chamada planetServices.list(PLANET.getTerrain(), PLANET.getClimate()) invoca o método list da classe PlanetServices com os parâmetros de terreno e clima do planeta.

3. A asserção verifica se a lista retornada está vazia.





## Removendo Planetas por ID


## Testando a remoção de um planeta com ID existente:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahenzkyzqb8mwkeo4m9u.png)

1. A expressão assertThatCode(() -> planetServices.deletePlanet(1L)).doesNotThrowAnyException() verifica se a chamada ao método deletePlanet com o ID 1L não lança nenhuma exceção.





## Testando a remoção de um planeta com ID inexistente:



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ikqogiuhgjbber553zl5.png)

1. A configuração doThrow(new RuntimeException()).when(planetRepository).deleteById(99L) instrui o mock planetRepository a lançar uma exceção do tipo RuntimeException quando o método deleteById for chamado com o argumento 99L.

2. A expressão assertThatThrownBy(() -> planetServices.deletePlanet(99L)).isInstanceOf(RuntimeException.class) verifica se a chamada ao método deletePlanet com o ID 99L lança uma exceção do tipo esperado.




## **Conclusão**

Neste artigo, exploramos um conjunto abrangente de testes unitários que cobrem diferentes cenários de uso da classe PlanetServices. 

Usando o JUnit 5 e o Mockito, conseguimos testar a criação, busca, listagem e remoção de planetas em um ambiente controlado.

###  A prática de escrever testes robustos não apenas ajuda a identificar problemas de código, mas também proporciona confiança na qualidade do software produzido.

 O uso conjunto do JUnit 5 e do Mockito demonstra como realizar testes eficazes em ambientes de desenvolvimento Java.