Introdução
A um tempo fui rodar um teste que tinha a anotação @SpringBootTest
e quando começou a rodar, já apareceu o Banner do Spring Boot
e uma mensagem:
contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader'
Quando eu vi isso, eu já vi que eu nem sabia o por quê de utilizar essa anotação e o qual deveria usar. Então o objetivo
deste post é de mostrar quais devem ser utilizadas pra qual cenário especifico.
Setup Inicial
Para o nosso setup inicial, vamos utilizar o mínimo e o pom vai precisar das seguintes depêndencias:
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
a exclusão do junit-vintage-engine
é necessária para utilização do JUnit5 que utiliza o junit-jupiter-engine
.
O uso do H2 vai ser para testar a camada de dados.
Camada de serviços e DTOs
Vamos criar uma classe bem simples pra fazer alguns testes, para isso vamos criar um usuário:
public class User { private String name; private int age; private String document; public User(String name, int age, String document) { this.name = name; this.age = age; this.document = document; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getDocument() { return document; } public void setDocument(String document) { this.document = document; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", document='" + document + '\'' + '}'; } }
e o teste que vai executar sobre essa classe vai ser:
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class EntityTest { @Test public void UserTest() { User user = new User("name", 12, "DOC12543"); assertEquals("name", user.name); assertTrue(user.toString().contains("User{")); } }
E deste jeito já tá rodando os nossos testes em 135ms enquanto se utilizarmos a notação @SpringBootTest
roda em 459ms,
parece pouco mas vamos lembrar que esse teste é o mais simples e nossa aplicação não tem nada configurado. Mesmo assim
a diferenção entre os dois testes já foi mais que o triplo, isso só pra validar um DTO.
Camada de dados
Para testar nossa camada de dados vamos dar colocar em nossa classe User
um atributo @Entity
e o ID que fica obrigatório:
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private int age; private String document; public User(String name, int age, String document) { this.name = name; this.age = age; this.document = document; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getDocument() { return document; } public void setDocument(String document) { this.document = document; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", document='" + document + '\'' + '}'; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } }
E o acesso vamos fazer via JpaRepository
com apenas um método:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long> { public User findUserByNameAndDocument(String name, String doc); }
Depois de ter criado essas classes já podemos criar alguns testes para essa parte. Alguns exemplos podem ser vistos a seguir:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(SpringExtension.class) @DataJpaTest public class UserRepositoryTests { @Autowired private UserRepository userRepository; @Test public void insertUser() { User user = new User("name", 11, "DOC12355"); userRepository.save(user); Integer countUser = userRepository.findAll().size(); assertEquals(1, countUser); } @Test public void checkUserSavedWithDocument() { User user = new User("name", 11, "DOC12355"); userRepository.save(user); Integer countUser = userRepository.findAll().size(); assertEquals(1, countUser); User user1 = userRepository.findUserByNameAndDocument("name", "DOC12355"); assertNotNull(user1); assertEquals(user, user1); } @Test public void checkUserSavedWithDocumentPassingOtherDocumentShouldReturnNull() { User user = new User("name", 11, "DOC12355"); userRepository.save(user); Integer countUser = userRepository.findAll().size(); assertEquals(1, countUser); User user1 = userRepository.findUserByNameAndDocument("name", "99999"); assertNull(user1); } }
Utilizando @DataJpaTest
ajuda a configurar algumas coisas automagicamente:
- Configuração de H2 in-memory
- Spring Data, Datasource
- Modo de logar o SQL ON
o modo de logar o sql para o nosso caso fica assim:
insert into user (age, document, name, id) values (?, ?, ?, ?) select user0_.id as id1_0_, user0_.age as age2_0_, user0_.document as document3_0_, user0_.name as name4_0_ from user user0_ select user0_.id as id1_0_, user0_.age as age2_0_, user0_.document as document3_0_, user0_.name as name4_0_ from user user0_ where user0_.name=? and user0_.document=?
Esse teste com @SpringBootTest
não roda.
Camada Web
Para camada web a gente já fez um post aqui
mas para o nosso caso de usuário vamos criar um controller para usuário, o mais simples seria o que retorna todos os usuários. Ficaria mais ou menos assim:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller public class UserController { @Autowired private UserRepository userRepository; @GetMapping(value = "/users") @ResponseBody public List<User> findAllUsers() { return userRepository.findAll(); } }
E um teste simples para resposta desse controller seria:
import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import java.util.List; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(SpringExtension.class) @WebMvcTest(controllers = UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserRepository userRepository; @Test public void findAllUsers() throws Exception { User user = new User("teste", 25, "DOC12346"); List<User> userList = List.of(user); when(userRepository.findAll()).thenReturn(userList); this.mockMvc.perform(get("/users")) .andExpect(status().isOk()) .andExpect(content().string(containsString("DOC12346"))); } }
Dentro desse teste utilizamos o MockMvc para fazer a chamada do controller, lembrando que isso é uma chamada "fake",
e o @MockBean
para executar um mock do nosso repository. Outra coisa que fizemos foi adicionar.
Conclusão
Além destes existem mais algumas configurações automaticas que você pode checar aqui,
mas sempre surgem novas ferramentas. Mas utilizar a abordagem correta para parte que você deseja testar, vai facilitar o uso
além de te dar velocidade, tanto na hora de rodar, quanto na hora do desenvolvimento
## Gostou?
Se gostou aqui estão minhas redes sociais para acompanhar os novos posts.
Twitter: luizleite_
Twitch: coffee_and_code
Linkedin: luizleiteoliveira
dev.to: luizleite_
Top comments (0)