Testando no Spring Boot
1. Visão geral
Neste artigo, daremos uma olhada emwriting tests using the framework support in Spring Boot. Cobriremos testes de unidade que podem ser executados isoladamente, bem como testes de integração que inicializarão o contexto do Spring antes de executar os testes.
Se você é novo no Spring Boot, verifique nossointro to Spring Boot.
Leitura adicional:
Explorando o Spring Boot TestRestTemplate
Aprenda a usar o novo TestRestTemplate no Spring Boot para testar uma API simples.
Guia rápido para @RestClientTest na Inicialização do Spring
Um guia rápido e prático para a anotação @RestClientTest no Spring Boot
Injeção de mockock Mockito nos feijões da primavera
Este artigo mostrará como usar a injeção de dependência para inserir zombarias Mockito no Spring Beans para teste de unidade.
2. Configuração do Projeto
O aplicativo que vamos usar neste artigo é uma API que fornece algumas operações básicas em um recursoEmployee. Esta é uma arquitetura em camadas típica - a chamada da API é processada da camadaController paraService para a camadaPersistence.
3. Dependências do Maven
Vamos primeiro adicionar nossas dependências de teste:
org.springframework.boot
spring-boot-starter-test
test
2.1.6.RELEASE
com.h2database
h2
test
1.4.194
Ospring-boot-starter-test é a dependência primária que contém a maioria dos elementos necessários para nossos testes.
OH2 DB é nosso banco de dados na memória. Isso elimina a necessidade de configurar e iniciar um banco de dados real para fins de teste.
4. Teste de integração com@DataJpaTest
Vamos trabalhar com uma entidade chamadaEmployee que temid ename como propriedades:
@Entity
@Table(name = "person")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Size(min = 3, max = 20)
private String name;
// standard getters and setters, constructors
}
E aqui está nosso repositório - usando Spring Data JPA:
@Repository
public interface EmployeeRepository extends JpaRepository {
public Employee findByName(String name);
}
Isso é tudo para o código da camada de persistência. Agora vamos escrever nossa aula de teste.
Primeiro, vamos criar o esqueleto de nossa classe de teste:
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private EmployeeRepository employeeRepository;
// write test cases here
}
@RunWith(SpringRunner.class) é usado para fornecer uma ponte entre os recursos de teste do Spring Boot e JUnit. Sempre que estivermos usando qualquer recurso de teste do Spring Boot em nossos testes JUnit, essa anotação será necessária.
@DataJpaTest fornece alguma configuração padrão necessária para testar a camada de persistência:
-
configurando H2, um banco de dados na memória
-
configurando Hibernate, Spring Data eDataSource
-
realizando um@EntityScan
-
ativando o log SQL
Para executar alguma operação do banco de dados, precisamos de alguns registros já configurados em nosso banco de dados. Para configurar esses dados, podemos usarTestEntityManager.The TestEntityManager provided by Spring Boot is an alternative to the standard JPA EntityManager that provides methods commonly used when writing tests.
EmployeeRepository é o componente que vamos testar. Agora vamos escrever nosso primeiro caso de teste:
@Test
public void whenFindByName_thenReturnEmployee() {
// given
Employee alex = new Employee("alex");
entityManager.persist(alex);
entityManager.flush();
// when
Employee found = employeeRepository.findByName(alex.getName());
// then
assertThat(found.getName())
.isEqualTo(alex.getName());
}
No teste acima, estamos usandoTestEntityManager para inserir umEmployee no banco de dados e lendo-o por meio da API find by name.
A parteassertThat(…) vem deAssertj library que vem junto com o Spring Boot.
5. Zombando com@MockBean
Nosso código de camadaService depende de nossoRepository. No entanto, para testar a camadaService, não precisamos saber ou nos preocupar sobre como a camada de persistência é implementada:
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}
Idealmente, devemos poder escrever e testar nosso código da camada de Serviço sem cabeamento em nossa camada de persistência completa.
Para conseguir isso,we can use the mocking support provided by Spring Boot Test.
Vamos dar uma olhada no esqueleto da classe de teste primeiro:
@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {
@TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {
@Bean
public EmployeeService employeeService() {
return new EmployeeServiceImpl();
}
}
@Autowired
private EmployeeService employeeService;
@MockBean
private EmployeeRepository employeeRepository;
// write test cases here
}
Para verificar a classeService, precisamos ter uma instância da classeService criada e disponível como@Bean para que possamos@Autowire em nossa classe de teste. Essa configuração é obtida usando a anotação@TestConfiguration.
Durante a varredura de componentes, podemos encontrar componentes ou configurações criados apenas para testes específicos acidentalmente capturados em qualquer lugar. Para ajudar a prevenir isso,Spring Boot provides @TestConfiguration annotation that can be used on classes in src/test/java to indicate that they should not be picked up by scanning.
Outra coisa interessante aqui é o uso de@MockBean. Écreates a Mock paraEmployeeRepository que pode ser usado para ignorar a chamada para oEmployeeRepository real:
@Before
public void setUp() {
Employee alex = new Employee("alex");
Mockito.when(employeeRepository.findByName(alex.getName()))
.thenReturn(alex);
}
Como a configuração está concluída, o caso de teste será mais simples:
@Test
public void whenValidName_thenEmployeeShouldBeFound() {
String name = "alex";
Employee found = employeeService.getEmployeeByName(name);
assertThat(found.getName())
.isEqualTo(name);
}
6. Teste de Unidade com@WebMvcTest
NossoController depende da camadaService; vamos incluir apenas um único método para simplificar:
@RestController
@RequestMapping("/api")
public class EmployeeRestController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/employees")
public List getAllEmployees() {
return employeeService.getAllEmployees();
}
}
Como estamos focados apenas no códigoController, é natural simular o código da camadaService para nossos testes de unidade:
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private EmployeeService service;
// write test cases here
}
Para testar oControllers, podemos usar@WebMvcTest. Ele irá configurar automaticamente a infraestrutura do Spring MVC para nossos testes de unidade.
Na maioria dos casos, @WebMvcTest será limitado para inicializar um único controlador. É usado junto com@MockBean para fornecer implementações simuladas para dependências necessárias.
@WebMvcTest também configura automaticamenteMockMvc, que oferece uma maneira poderosa de testar facilmente controladores MVC sem iniciar um servidor HTTP completo.
Dito isso, vamos escrever nosso caso de teste:
@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
Employee alex = new Employee("alex");
List allEmployees = Arrays.asList(alex);
given(service.getAllEmployees()).willReturn(allEmployees);
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name", is(alex.getName())));
}
A chamada do métodoget(…) pode ser substituída por outros métodos correspondentes a verbos HTTP comoput(),post(), etc. Observe que também estamos definindo o tipo de conteúdo na solicitação.
MockMvc é flexível e podemos criar qualquer solicitação usando-o.
7. Teste de integração com@SpringBootTest
Como o nome sugere, os testes de integração se concentram na integração de diferentes camadas do aplicativo. Isso também significa que não há zombaria.
Ideally, we should keep the integration tests separated from the unit tests and should not run along with the unit tests. Podemos fazer isso usando um perfil diferente para executar apenas os testes de integração. Algumas razões para fazer isso podem ser que os testes de integração são demorados e podem precisar de um banco de dados real para serem executados.
No entanto, neste artigo, não vamos nos concentrar nisso e, em vez disso, usaremos o armazenamento de persistência H2 na memória.
Os testes de integração precisam iniciar um contêiner para executar os casos de teste. Portanto, é necessária alguma configuração adicional para isso - tudo isso é fácil no Spring Boot:
@RunWith(SpringRunner.class)
@SpringBootTest(
SpringBootTest.WebEnvironment.MOCK,
classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource(
locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@Autowired
private EmployeeRepository repository;
// write test cases here
}
A anotação@SpringBootTest pode ser usada quando precisamos inicializar o contêiner inteiro. A anotação funciona criando oApplicationContext que será utilizado em nossos testes.
Podemos usar o atributowebEnvironment de@SpringBootTest para configurar nosso ambiente de execução; estamos usandoWebEnvironment.MOCK aqui - para que o contêiner opere em um ambiente de servlet simulado.
Podemos usar a anotação@TestPropertySource para configurar os locais dos arquivos de propriedades específicos para nossos testes. Observe que o arquivo de propriedades carregado com@TestPropertySource substituirá o arquivoapplication.properties existente.
Oapplication-integrationtest.properties contém os detalhes para configurar o armazenamento de persistência:
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Se quisermos executar nossos testes de integração no MySQL, podemos alterar os valores acima no arquivo de propriedades.
Os casos de teste para os testes de integração podem ser semelhantes aos testes de unidade da camadaController:
@Test
public void givenEmployees_whenGetEmployees_thenStatus200()
throws Exception {
createTestEmployee("bob");
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].name", is("bob")));
}
A diferença dos testes de unidade da camadaController é que aqui nada é simulado e os cenários de ponta a ponta serão executados.
8. Testes configurados automaticamente
Um dos recursos incríveis das anotações configuradas automaticamente do Spring Boot é que ele ajuda a carregar partes do aplicativo completo e testar camadas específicas da base de código.
Além das anotações mencionadas acima, aqui está uma lista de algumas anotações amplamente utilizadas:
-
@WebFluxTest– we pode usar a anotação@WebFluxTest para testar os controladores Spring Webflux. Muitas vezes é usado junto com@MockBean para fornecer implementações simuladas para dependências necessárias.
-
@JdbcTest – we pode usar a anotação@JdbcTest para testar aplicativos JPA, mas é para testes que requerem apenas umDataSource. A anotação configura um banco de dados embutido na memória e umJdbcTemplate.
-
@JooqTest – Para testar testes relacionados ao jOOQ, podemos usar a anotação@JooqTest, que configura um DSLContext.
-
@DataMongoTest – Para testar aplicativos MongoDB@DataMongoTest é uma anotação útil. Por padrão, ele configura um MongoDB integrado na memória se o driver estiver disponível por meio de dependências, configura varreduras deMongoTemplate, para classes@Document e configura os repositórios do Spring Data MongoDB.
-
@DataRedisTest – makes it easier to test AplicativosRedis. Ele verifica as classes@RedisHash e configura os repositórios do Spring Data Redis por padrão.
-
@DataLdapTest – configura umLDAP incorporado na memória (se disponível), configura umLdapTemplate, verifica as classes@Entry e configura os repositórios Spring DataLDAP por padrão
-
@RestClientTest – we geralmente usa a anotação@RestClientTest para testar clientes REST. Ele configura automaticamente diferentes dependências, como suporte a Jackson, GSON e Jsonb, configura aRestTemplateBuilder e adiciona suporte paraMockRestServiceServer por padrão.
9. Conclusão
Neste tutorial, aprofundamos o suporte a testes no Spring Boot e mostramos como gravar testes de unidade com eficiência.
O código-fonte completo deste artigo pode serfound over on GitHub. O código fonte contém muitos outros exemplos e vários casos de teste.
E, se você quiser continuar aprendendo sobre testes, temos artigos separados relacionados aintegration testseunit tests in JUnit 5.