Escrevendo Testes para o Blog Pessoal

Overview

Implementando testes com o Spring Testing no Blog Pessoal

Nesta atividade iremos implementar os testes nas Camadas Model, Repository e Controller da Classe Usuário.

Utilize o e-book sobre SpringTesting (Clique aqui) e as instruções descritas abaixo como referências para implementar os testes nas 3 Camadas da Classe Usuario.

Boas Práticas

  1. Configure as Dependências no arquivo pom.xml
  2. Configure o Banco de Dados (db_blogpessoaltest)
  3. Prepare a estrutura de pacotes para os testes
  4. Crie os métodos construtores na Classe Usuario
  5. Crie a Classe de testes na Camada Model: UsuarioTest
  6. Crie a Classe de testes na Camada Repository: UsuarioRepositoryTest
  7. Crie a Classe de testes na Camada Controller: UsuarioControllerTest
  8. Execute os testes
  9. Referências

Dependências

No arquivo, pom.xml, vamos alterar a linha:

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-test</artifactId>
    	<scope>test</scope>
    </dependency>

Para:

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-test</artifactId>
    	<scope>test</scope>
    	<exclusions>
    		<exclusion>
    			<groupId>org.junit.vintage</groupId>
    			<artifactId>junit-vintage-engine</artifactId>
    		</exclusion>
    	</exclusions>
    </dependency>

*Essa alteração irá ignorar as versões anteriores ao JUnit 5 (vintage).

Banco de Dados

Agora vamos configurar um Banco de dados de testes para não usar o Banco de dados principal.

  1. No lado esquerdo superior, na Guia Package Explorer, clique sobre a pasta do projeto com o botão direito do mouse e clique na opção New->Source folder
  1. Em Source Folder, no item Folder name, informe o caminho como mostra a figura abaixo (src/test/resources), e clique em Finish:
  1. Na nova Source Folder (src/test/resources) , crie o arquivo application.properties, para configurarmos a conexão com o Banco de Dados de testes

  2. No lado esquerdo superior, na Guia Package explorer, na Package src/test/resources, clique com o botão direito do mouse e clique na opção New->File.

  3. Em File name, digite o nome do arquivo (application.properties) e clique em Finish.

  1. Veja o arquivo criado na Package Explorer
  1. Insira no arquivo application.properties as seguinte linhas:
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://localhost/db_testeblogpessoal?createDatabaseIfNotExist=true&serverTimezone=America/Sao_Paulo&useSSl=false
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.show-sql=true

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Brazil/East

Observe que o nome do Banco de dados possui a palavra teste para indicar que será apenas para a execução dos testes.

Não esqueça de configurar a senha do usuário root.

Estrutura de pacotes

Na Source Folder de Testes (src/test/java) , observe que existe uma estrutura de pacotes idêntica a Source Folder Main (src/main/java). Crie na Source Folder de Testes as packages Model, Repository e Controller como mostra a figura abaixo.

O Processo de criação dos arquivos é o mesmo do código principal, exceto o nome dos arquivos que deverão ser iguais aos arquivos da Source Folder Main (src/main/java) acrescentando a palavra Test no final como mostra a figura abaixo.

Exemplo: UsuarioRepository -> UsuarioRepositoryTest.

Métodos Construtores na Classe Usuario

Na Classe Usuario, na canada Model, vamos criar 2 métodos construtores: o primeiro com todos os atributos, exceto o postagens e um segundo método vazio, ou seja, sem atributos.

package br.org.generation.blogpessoal.model;

import java.time.LocalDate;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "tb_usuarios")
public class Usuario {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	
	@NotNull(message = "O atributo nome é obrigatório")
	@Size(min = 5, max = 100, message = "O atributo nome deve conter no mínimo 05 e no máximo 100 caracteres")
	private String nome;
	
	@NotNull(message = "O atributo usuário é obrigatório")
	@NotBlank(message = "O atributo usuário não pode ser vazio")
	@Email(message = "O atributo usuário deve ser um email")
	private String usuario;
	
	@NotNull(message = "O atributo senha é obrigatório")
	@Size(min = 8, message = "O atributo senha deve ter no mínimo 8 caracteres")
	private String senha;
	
	@Column(name = "dt_nascimento")
	@JsonFormat(pattern="yyyy-MM-dd")
    private LocalDate dataNascimento;
	
	@OneToMany (mappedBy = "usuario", cascade = CascadeType.REMOVE)
	@JsonIgnoreProperties("usuario")
	private List <Postagem> postagem;

	// Primeiro método Construtor

	public Usuario(long id, String nome, String usuario, String senha, LocalDate dataNascimento) {
		this.id = id;
		this.nome = nome;
		this.usuario = usuario;
		this.senha = senha;
		this.dataNascimento = dataNascimento;
	}

	// Segundo método Construtor

	public Usuario() {	}


	public long getId() {
		return this.id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getNome() {
		return this.nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getUsuario() {
		return this.usuario;
	}

	public void setUsuario(String usuario) {
		this.usuario = usuario;
	}

	public String getSenha() {
		return this.senha;
	}

	public void setSenha(String senha) {
		this.senha = senha;
	}

	public LocalDate getDataNascimento() {
		return this.dataNascimento;
	}

	public void setDataNascimento(LocalDate dataNascimento) {
		this.dataNascimento = dataNascimento;
	}

	public List<Postagem> getPostagem() {
		return this.postagem;
	}

	public void setPostagem(List<Postagem> postagem) {
		this.postagem = postagem;
	}

}

Classe UsuarioTest

A Classe UsuarioTest será utilizada parta testar a Classe Model do Usuario. Crie a classe UsuarioTest na package model, dentro da Source Folder de Testes (src/test/java)

Importante: O Teste da Classe Usuario da camada Model, não utiliza o Banco de Dados.

package br.org.generation.blogpessoal.model;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class UsuarioTest {
    
    public Usuario usuario;
    public Usuario usuarioErro = new Usuario();

	@Autowired
	private  ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
	
	Validator validator = factory.getValidator();

	@BeforeEach
	public void start() {

		LocalDate data = LocalDate.parse("2000-07-22", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
		usuario = new Usuario(0L, "João da Silva", "[email protected]", "13465278", data);

	}

	@Test
	@DisplayName("✔ Valida Atributos Não Nulos")
	void testValidaAtributos() {

		Set<ConstraintViolation<Usuario>> violacao = validator.validate(usuario);
		
		System.out.println(violacao.toString());

		assertTrue(violacao.isEmpty());
	}
    
    @Test
	@DisplayName("✖ Não Valida Atributos Nulos")
	void  testNaoValidaAtributos() {

		Set<ConstraintViolation<Usuario>> violacao = validator.validate(usuarioNulo);
		System.out.println(violacao.toString());

		assertTrue(violacao.isEmpty());
	}

}

💥 Para inserir os emojis na annotation @DisplayName, utilize as teclas de atalho Windows + Ponto

Classe UsuarioRepositoryTest

A Classe UsuarioRepositoryTest será utilizada parta testar a Classe Repository do Usuario. Crie a classe UsuarioRepositoryTest na package repository, na Source Folder de Testes (src/test/java)

Importante: O Teste da Classe UsuarioRepository da camada Repository, utiliza o Banco de Dados, entretanto ele não criptografa a senha ao gravar um novo usuario no Banco de dados. O teste não utiliza a Classe de Serviço UsuarioService para gravar o usuário. O Teste utiliza o método save(), da Classe JpaRepository de forma direta.

package br.org.generation.blogpessoal.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

import br.org.generation.blogpessoal.model.Usuario;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UsuarioRepositoryTest {
    
    @Autowired
	private UsuarioRepository usuarioRepository;
	
	@BeforeAll
	void start() throws ParseException {
	   
		LocalDate data = LocalDate.parse("2000-07-22", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
		
		Usuario usuario = new Usuario(0, "João da Silva", "[email protected]", "13465278", data);
		if(!usuarioRepository.findByUsuario(usuario.getUsuario()).isPresent())
			usuarioRepository.save(usuario);
		
		usuario = new Usuario(0, "Manuel da Silva", "[email protected]", "13465278", data);
		if(!usuarioRepository.findByUsuario(usuario.getUsuario()).isPresent())
			usuarioRepository.save(usuario);
		
		usuario = new Usuario(0, "Frederico da Silva", "[email protected]", "13465278", data);
		if(!usuarioRepository.findByUsuario(usuario.getUsuario()).isPresent())
			usuarioRepository.save(usuario);

        usuario = new Usuario(0, "Paulo Antunes", "[email protected]", "13465278", data);
        if(!usuarioRepository.findByUsuario(usuario.getUsuario()).isPresent())
            usuarioRepository.save(usuario);
	}
	
	@Test
	@DisplayName("💾 Retorna o nome")
	public void findByNomeRetornaNome() throws Exception {

		Usuario usuario = usuarioRepository.findByNome("João da Silva");
		assertTrue(usuario.getNome().equals("João da Silva"));
	}
	
	@Test
	@DisplayName("💾 Retorna 3 usuarios")
	public void findAllByNomeContainingIgnoreCaseRetornaTresUsuarios() {

		List<Usuario> listaDeUsuarios = usuarioRepository.findAllByNomeContainingIgnoreCase("Silva");
		assertEquals(3, listaDeUsuarios.size());
	}

	@AfterAll
	public void end() {
		
		usuarioRepository.deleteAll();
		
	}
}

Classe UsuarioControllerTest

A Classe UsuarioControllerTest será utilizada parta testar a Classe Controller do Usuario. Crie a classe UsuarioControllerTest na package controller, na Source Folder de Testes (src/test/java)

Importante: Para executar todos os testes da Classe UsuarioControllerTest de uma única vez, faça o Drop do Banco de Dados de testes antes. Caso contrário será necessário verificar o id do usuário que será alterado pelo método PUT no banco de dados via MySQL Workbench antes de executar teste.

package br.org.generation.blogpessoal.controller;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestMethodOrder;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import br.org.generation.blogpessoal.model.Usuario;
import br.org.generation.blogpessoal.repository.UsuarioRepository;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class UsuarioControllerTest {
    
	@Autowired
	private TestRestTemplate testRestTemplate;
	
	private Usuario usuario;
	private Usuario usuarioUpdate;
	private Usuario usuarioAdmin;

	@Autowired
	private UsuarioRepository usuarioRepository;
	
	@BeforeAll
	public void start() throws ParseException {

		LocalDate dataAdmin = LocalDate.parse("1990-07-22", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        usuarioAdmin = new Usuario(0L, "Administrador", "[email protected]", "admin123", dataAdmin);

		if(!usuarioRepository.findByUsuario(usuarioAdmin.getUsuario()).isPresent()) {

            HttpEntity<Usuario> request = new HttpEntity<Usuario>(usuarioAdmin);
			testRestTemplate.exchange("/usuarios/cadastrar", HttpMethod.POST, request, Usuario.class);
			
		}
		
		LocalDate dataPost = LocalDate.parse("2000-07-22", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        usuario = new Usuario(0L, "Paulo Antunes", "[email protected]", "13465278", dataPost);
        
		LocalDate dataPut = LocalDate.parse("2000-07-22", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        usuarioUpdate = new Usuario(2L, "Paulo Antunes de Souza", "[email protected]", "souza123", dataPut);
	}

	@Test
	@Order(1)
    @DisplayName("✔ Cadastrar Usuário!")
	public void deveRealizarPostUsuario() {

		HttpEntity<Usuario> request = new HttpEntity<Usuario>(usuario);

		ResponseEntity<Usuario> resposta = testRestTemplate.exchange("/usuarios/cadastrar", HttpMethod.POST, request, Usuario.class);
		
		assertEquals(HttpStatus.CREATED, resposta.getStatusCode());

	}

	@Test
	@Order(2)
    @DisplayName("👍 Listar todos os Usuários!")
	public void deveMostrarTodosUsuarios() {
		
		ResponseEntity<String> resposta = testRestTemplate.withBasicAuth("[email protected]", "admin123").exchange("/usuarios/all", HttpMethod.GET, null, String.class);
		
		assertEquals(HttpStatus.OK, resposta.getStatusCode());
	}
	
	@Test
    @Order(3)
	@DisplayName("😳 Alterar Usuário!")
	public void deveRealizarPutUsuario() {

		HttpEntity<Usuario> request = new HttpEntity<Usuario>(usuarioUpdate);

		ResponseEntity<Usuario> resposta = testRestTemplate.withBasicAuth("[email protected]", "admin123").exchange("/usuarios/alterar", HttpMethod.PUT, request, Usuario.class);
		
		assertEquals(HttpStatus.OK, resposta.getStatusCode());
		
	}
	
}

Observe que como habilitamos em nosso Blog Pessoal o Spring Security com autenticação do tipo BasicAuth na API, o Objeto testRestTemplate deverá passar um usuário e a sua respectiva senha para realizar os testes.

Exemplo:

ResponseEntity<String> resposta = testRestTemplate
.withBasicAuth("[email protected]", "admin123")
.exchange("/usuarios/all", HttpMethod.GET, null, String.class);

Executando os testes no Eclipse / STS

  1. No lado esquerdo superior, na Guia Project, na Package src/test/java, clique com o botão direito do mouse sobre o teste que você deseja executar e clique na opção Run As->JUnit Test.
  1. Para acompanhar os testes, ao lado da Guia Project, clique na Guia JUnit.
  1. Se todos os testes passarem, a Guia do JUnit ficará com uma faixa verde (janela 01). Caso algum teste não passe, a Guia do JUnit ficará com uma faixa vermelha (janela 02). Neste caso, observe o item Failure Trace para identificar o (s) erro (s).
Janela 01: Testes aprovados.
Janela 02: Testes reprovados.
Ao escrever testes, sempre verifique se a importação dos pacotes do JUnit na Classe de testes estão corretos. O JUnit 5 tem como pacote base org.junit.jupiter.api.

Referências

Documentação Oficial do Spring Testing

Página Oficial do JUnit5

Documentação Oficial do JUnit 5

You might also like...

Repositorio para el Proyecto PSA

proyecto-psa Status Ejecución Test Manual: Status Ejecución Test Automático: 1. Repositorio para el Proyecto PSA Se propone una estructura de mono-rep

Nov 16, 2021

Jogo criado em java para disciplina de Linguagem de Programação III

Jogo criado em java para disciplina de Linguagem de Programação III

Mr.Cat-JOGO Jogo criado em java para disciplina de Linguagem de Programação III O jogo é um cookie Clicker feito em Java para disciplina de Linguagem

Dec 22, 2021

Distributed-messenger - 👨‍👨‍👧 Sistema de chat utilizando sockets desenvolvido para a disciplina de Sistemas Paralelos e Distribuídos

Distributed Messenger 👨‍👨‍👧 Sistema de chat utilizando sockets desenvolvido para a disciplina de Sistemas Paralelos e Distribuídos Requisitos do Pr

Feb 19, 2022

Minha metas para 2022.

Minha metas para 2022.

Minhas metas para 2022 Essas metas são pessoais, para desenvolvimento e aprimoramento de habilidades sociais, mas também em conhecimento tecnico e con

Jun 9, 2022

Twiscord es una simple aplicación que permite conectar Twitter y Discord para poder publicar cosas en ambas plataformas a la vez.

Twiscord Twiscord es una simple aplicación que permite conectar Twitter y Discord para poder publicar en ambas plataformas a la vez dedicado a streame

Jan 10, 2022

App para sortear números aleatórios de 0 a 10

App para sortear números aleatórios de 0 a 10

SorteioAPP- App para sortear números aleatórios de 0 a 10 Projeto desenvolvido com os conhecimentos adquiridos nas aulas do curso de Desenvolvimento A

Mar 25, 2022

Repositório para desenvolvimento da camada de back-end da aplicação

Projeto Integrador - Back-end 🚀 Começando Essas instruções permitirão que você obtenha uma cópia do projeto em operação na sua máquina local para fin

Jun 2, 2022

Desafio Alura Challenge para backend. Criando uma API REST de controle de orçamento utilizando JAVA.

Desafio Alura Challenge para backend. Criando uma API REST de controle de orçamento utilizando JAVA.

Jun 16, 2022

Projeto de demonstração em JAVA, para administração e controle de um estacionamento

projetoEstacionamento Projeto de demonstração em JAVA, para administração e controle de um estacionamento: Este software de demonstração tem como obje

Sep 22, 2022
Owner
Rafael Queiróz
Rafael Queiróz
Repositório referente ao código de uma classe data, com testes JUNIT, classe de exceção própria e classe aplicação para demonstrar as diversas funcionalidades da classe data

Exercicio-Data Repositório referente ao código de uma classe data, com testes JUNIT, classe de exceção própria e classe aplicação para demonstrar as d

Bruno Silveira Cequeira Lima 3 May 4, 2021
Repositório focado para a Turma 2022.2 do Cesmac para disciplina Programação Orientada a Objeto

Sobre Repositório focado para turma de POO - Cesmac - 2022.2 Links úteis IntelliJ Download Java Doc - JDK 18 2Devs Podcast: SimpleCast Spotify Como se

Rachid Calazans 12 Dec 8, 2022
Java based open source static site/blog generator for developers & designers.

JBake JBake is a Java based open source static site/blog generator for developers. Documentation Full documentation is available on jbake.org. Contrib

JBake 1k Dec 30, 2022
🦄 Best beautiful java blog, worth a try

Tale Blog Tale's English meaning for the Story, I believe that every person who insists on writing a blog is a story; Chinese you call it Collapse doe

Tale Blog System 4.8k Dec 30, 2022
A simple blog post api made with spring,mysql.Following tutorial by @FadatareRamesh(Java Guides)

blogAPI A simple blog post api made with spring,mysql.Following tutorial by @FadatareRamesh(Java Guides) Frontend server(made using Angular) can be fo

null 1 Feb 2, 2022
Spring MVC backend written in Java for my wiki/blog

blog-api Spring MVC backend written in Java for my wiki/blog. Why Spring? Spring MVC and other parts of the Spring framework are still immensely popul

null 0 Mar 16, 2022
A blog recipes API for you to share and search for food recipes

A blog recipes API for you to share and search for food recipes

java dojo 3 Apr 15, 2022
A personal blog based on Vue+SpringBoot+MySql+Redis+Shiro+JWT

项目:Vue-SpringBoot-PersonalBlog 个人博客网址:http://www.huchao.vip/blogs CSDN:毛_三月 介绍 一个基于SpringBoot + Vue+MybatisPlus+Shiro+JWT+Redis开发的前后端分离博客项目,带有超级详细开发文档

Chao. Hu 26 Dec 20, 2022
Application for creating blog posts, developed with Java using Spring Framework for backend and Angular along with PrimeNG Library for frontend development.

Application for creating blog posts, developed with Java using Spring Framework for backend and Angular along with PrimeNG Library for frontend development.

Áureo Carmelino 10 Nov 27, 2022
Projeto para consulta de filmes na API data IMDB e construção de playlists.

Projeto IDP: Filmes API ✍️ API Requirements Must to have Comunicar com uma API externa (desenvolvida pelo colaborador ou por terceiros). Persistir dad

Sarah Andrade Toscano de Carvalho 2 Oct 9, 2022