Introdução

Os testes de integração são essenciais para garantir que diferentes módulos de uma aplicação Java funcionem corretamente em conjunto. Neste tutorial, vamos explorar como implementar testes de integração em projetos construídos com Spring Boot e JUnit. Você aprenderá a configurar seu ambiente, a estruturar seus testes e a garantir que sua aplicação esteja pronta para produção. Com uma abordagem prática, apresentaremos exemplos claros que permitirão você integrar testes de forma eficaz em seu ciclo de desenvolvimento, proporcionando um código mais robusto e confiável. Este guia é ideal para desenvolvedores que buscam aprimorar suas habilidades em testes automatizados usando tecnologias atuais.

Etapas

  1. Configuração do Ambiente de Desenvolvimento

    Para começar, certifique-se de que o JDK (Java Development Kit) e o Maven estão instalados. Verifique as versões instaladas usando os comandos abaixo. Caso não tenha essas ferramentas, siga as instruções nos sites oficiais.

    commands
    # Verificar versões instaladas
    java -version
    mvn -version

  2. Criação do Projeto Spring Boot

    Utilize o Spring Initializr para gerar um novo projeto Spring Boot com as dependências necessárias. Configure o projeto com as opções: Project: Maven, Language: Java, Spring Boot: última versão estável, Packaging: Jar. Adicione as dependências ‘Spring Web’, ‘Spring Data JPA’ e ‘H2 Database’. Baixe e descompacte o projeto em seu diretório de trabalho.

    pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.example</groupId>
      <artifactId>integracao-testes</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>integracao-testes</name>
      <description>Testes de Integração com Spring Boot</description>
      <properties>
        <java.version>11</java.version>
      </properties>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <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>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
        </plugins>
      </build>
    </project>

  3. Configuração do Banco de Dados H2

    Edite o arquivo `application.properties` para definir as propriedades do H2 e habilitar o console web.

    application.properties
    spring.h2.console.enabled=true
    spring.datasource.url=jdbc:h2:mem:testdb
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=
    spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
    spring.jpa.hibernate.ddl-auto=update

  4. Criação da Entidade `Produto`

    Implemente a classe modelo `Produto`, utilizando anotações JPA para mapear a classe à tabela no banco de dados.

    Produto.java
    package com.example.integracaotestes.model;
    
    import javax.persistence.*;
    
    @Entity
    @Table(name = "produtos")
    public class Produto {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(nullable = false)
        private String nome;
    
        @Column(nullable = false)
        private Double preco;
    
        // Getters e Setters
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getNome() {
            return nome;
        }
    
        public void setNome(String nome) {
            this.nome = nome;
        }
    
        public Double getPreco() {
            return preco;
        }
    
        public void setPreco(Double preco) {
            this.preco = preco;
        }
    }

  5. Criação do Repositório `ProdutoRepository`

    Implemente a interface `ProdutoRepository`, que estende `JpaRepository` e fornece métodos CRUD.

    ProdutoRepository.java
    package com.example.integracaotestes.repository;
    
    import com.example.integracaotestes.model.Produto;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface ProdutoRepository extends JpaRepository<Produto, Long> {
        // Métodos personalizados podem ser definidos aqui
    }

  6. Implementação do Serviço `ProdutoService`

    Crie a classe `ProdutoService`, que contém a lógica de negócios e usa o `ProdutoRepository` para interagir com o banco de dados.

    ProdutoService.java
    package com.example.integracaotestes.service;
    
    import com.example.integracaotestes.model.Produto;
    import com.example.integracaotestes.repository.ProdutoRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Optional;
    
    @Service
    public class ProdutoService {
        @Autowired
        private ProdutoRepository produtoRepository;
    
        public List<Produto> findAll() {
            return produtoRepository.findAll();
        }
    
        public Optional<Produto> findById(Long id) {
            return produtoRepository.findById(id);
        }
    
        public Produto save(Produto produto) {
            return produtoRepository.save(produto);
        }
    
        public Produto update(Long id, Produto produtoDetails) {
            Produto produto = produtoRepository.findById(id).orElseThrow(() -> new RuntimeException("Produto não encontrado"));
            produto.setNome(produtoDetails.getNome());
            produto.setPreco(produtoDetails.getPreco());
            return produtoRepository.save(produto);
        }
    
        public void deleteById(Long id) {
            produtoRepository.deleteById(id);
        }
    }

  7. Implementação do Controlador `ProdutoController`

    Crie a classe `ProdutoController` para expor os endpoints da API, permitindo operações CRUD.

    ProdutoController.java
    package com.example.integracaotestes.controller;
    
    import com.example.integracaotestes.model.Produto;
    import com.example.integracaotestes.service.ProdutoService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/api/produtos")
    public class ProdutoController {
        @Autowired
        private ProdutoService produtoService;
    
        @GetMapping
        public List<Produto> getAllProdutos() {
            return produtoService.findAll();
        }
    
        @GetMapping("/{id}")
        public ResponseEntity<Produto> getProdutoById(@PathVariable Long id) {
            return produtoService.findById(id)
                    .map(ResponseEntity::ok)
                    .orElse(ResponseEntity.notFound().build());
        }
    
        @PostMapping
        public Produto createProduto(@RequestBody Produto produto) {
            return produtoService.save(produto);
        }
    
        @PutMapping("/{id}")
        public ResponseEntity<Produto> updateProduto(@PathVariable Long id, @RequestBody Produto produtoDetails) {
            return ResponseEntity.ok(produtoService.update(id, produtoDetails));
        }
    
        @DeleteMapping("/{id}")
        public ResponseEntity<Void> deleteProduto(@PathVariable Long id) {
            produtoService.deleteById(id);
            return ResponseEntity.noContent().build();
        }
    }

  8. Implementação de Testes de Integração

    Crie a classe de testes `ProdutoControllerTest` usando o `@SpringBootTest` e `@AutoConfigureMockMvc` para validar os endpoints da API.

    ProdutoControllerTest.java
    package com.example.integracaotestes.controller;
    
    import com.example.integracaotestes.model.Produto;
    import com.example.integracaotestes.repository.ProdutoRepository;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    import java.util.List;
    
    @SpringBootTest
    @AutoConfigureMockMvc
    public class ProdutoControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        private ProdutoRepository produtoRepository;
    
        @BeforeEach
        public void setUp() {
            produtoRepository.deleteAll(); // Limpa o banco de dados antes de cada teste
        }
    
        @Test
        public void testCreateProduto() throws Exception {
            Produto produto = new Produto();
            produto.setNome("Produto Teste");
            produto.setPreco(10.0);
    
            mockMvc.perform(post("/api/produtos")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(new ObjectMapper().writeValueAsString(produto)))
                    .andExpect(status().isCreated());
        }
    
        @Test
        public void testGetAllProdutos() throws Exception {
            // Adiciona um produto no banco de dados para testar a recuperação
            produtoRepository.save(produto);
    
            mockMvc.perform(get("/api/produtos"))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.[0].nome").value("Produto Teste"));
        }
    }

  9. Executando a Aplicação e Testes

    Utilize o Maven para compilar e executar a aplicação. Em seguida, utilize ferramentas como Postman ou cURL para testar os endpoints da API.

    commands
    # Compilar e executar a aplicação
    mvn spring-boot:run
    # Executar os testes unitários
    mvn test

    curl_examples
    # Listar todos os produtos
    curl -X GET http://localhost:8080/api/produtos
    # Criar um novo produto
    curl -X POST -H "Content-Type: application/json" -d '{"nome":"Novo Produto", "preco":99.99}' http://localhost:8080/api/produtos

Conclusão

Neste tutorial, você aprendeu como implementar testes de integração em uma aplicação Java usando Spring Boot, cobrindo desde a configuração do ambiente até a execução de testes automatizados. Através de exemplos práticos, você entendeu como garantir que suas APIs funcionem conforme o esperado em diferentes cenários. Com essas práticas, você pode aumentar a qualidade do seu código, garantindo que todas as partes da sua aplicação interajam corretamente. Essa abordagem não apenas melhora a confiabilidade da sua aplicação, mas também facilita a manutenção futura, à medida que você continua a expandir suas habilidades em desenvolvimento de software.

Hashtags

#Java #SpringBoot #JUnit #TestesDeIntegracao #DesenvolvimentoDeSoftware