Introdução

Neste tutorial, abordaremos como implementar uma aplicação ágil utilizando Java com o Spring Framework, focando na construção de uma API RESTful que suporte testes automatizados com JUnit. O desenvolvimento ágil tem se tornado crucial em ambientes de software, pois permite entregas rápidas e adaptação contínua às necessidades dos usuários. Usaremos o Spring Boot para acelerar o processo de desenvolvimento e JUnit para garantir que nossa aplicação funcione corretamente através de testes unitários. Através de passos claros e exemplos de código, você entenderá como integrar essas tecnologias para criar uma aplicação robusta e eficiente. Este guia é ideal para desenvolvedores que desejam aprimorar suas habilidades em Java, testabilidade e práticas de desenvolvimento ágil.

Etapas

  1. Configuração do Ambiente de Desenvolvimento

    Para começar, é essencial ter o JDK (Java Development Kit) e o Maven instalados em sua máquina. Você pode verificar se estão instalados corretamente através dos comandos `java -version` e `mvn -version`. Se necessário, instale o JDK e o Maven de acordo com as instruções fornecidas nas documentações 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. Selecione as opções: **Project**: Maven, **Language**: Java, **Spring Boot**: versão estável mais recente, **Packaging**: Jar, **Java**: 11 ou superior. Adicione as dependências ‘Spring Web’, ‘Spring Data JPA’, e ‘Spring Boot Starter Test’. Após a configuração, baixe o projeto e extraia-o.

    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>api-agil</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>api-agil</name>
      <description>API Ágil 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>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>com.h2database</groupId>
          <artifactId>h2</artifactId>
          <scope>runtime</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

    Crie o arquivo `application.properties` na pasta `src/main/resources` e configure as propriedades do banco de dados H2 para habilitar o console web e definir a URL de conexão.

    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.hibernate.ddl-auto=update
    spring.jpa.show-sql=true

  4. Criação da Entidade `Cliente`

    Crie uma classe modelo `Cliente` que represente um cliente na aplicação. Utilize anotações JPA para mapear a classe à tabela do banco de dados, definindo os atributos correspondentes e seus mapeamentos.

    Cliente.java
    package com.example.apiagil.model;
    
    import javax.persistence.*;
    import java.util.Date;
    
    @Entity
    @Table(name = "clientes")
    public class Cliente {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(nullable = false)
        private String nome;
    
        @Column(nullable = false)
        private String email;
    
        @Temporal(TemporalType.DATE)
        @Column(nullable = false)
        private Date dataNascimento;
    
        // 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 String getEmail() { return email; }
        public void setEmail(String email) { this.email = email; }
        public Date getDataNascimento() { return dataNascimento; }
        public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; }
    }

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

    Implemente a interface `ClienteRepository`, estendendo `JpaRepository` para fornecer os métodos padrão de acesso aos dados e permitir operações CRUD.

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

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

    Crie a classe `ClienteService` que contém a lógica de negócios e utiliza o `ClienteRepository` para interagir com o banco de dados, implementando métodos para as operações da API.

    ClienteService.java
    package com.example.apiagil.service;
    
    import com.example.apiagil.model.Cliente;
    import com.example.apiagil.repository.ClienteRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Optional;
    
    @Service
    public class ClienteService {
        @Autowired
        private ClienteRepository clienteRepository;
    
        public List<Cliente> findAll() { return clienteRepository.findAll(); }
    
        public Optional<Cliente> findById(Long id) { return clienteRepository.findById(id); }
    
        public Cliente save(Cliente cliente) { return clienteRepository.save(cliente); }
    
        public Cliente update(Long id, Cliente clienteDetails) {
            Cliente cliente = clienteRepository.findById(id).orElseThrow(() -> new RuntimeException("Cliente não encontrado"));
            cliente.setNome(clienteDetails.getNome());
            cliente.setEmail(clienteDetails.getEmail());
            cliente.setDataNascimento(clienteDetails.getDataNascimento());
            return clienteRepository.save(cliente);
        }
    
        public void deleteById(Long id) { clienteRepository.deleteById(id); }
    }

  7. Implementação do Controlador `ClienteController`

    Implemente a classe `ClienteController` para expor os endpoints da API, permitindo operações CRUD dos clientes.

    ClienteController.java
    package com.example.apiagil.controller;
    
    import com.example.apiagil.model.Cliente;
    import com.example.apiagil.service.ClienteService;
    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/clientes")
    public class ClienteController {
        @Autowired
        private ClienteService clienteService;
    
        @GetMapping
        public List<Cliente> getAllClientes() { return clienteService.findAll(); }
    
        @GetMapping("/{id}")
        public ResponseEntity<Cliente> getClienteById(@PathVariable Long id) {
            return clienteService.findById(id)
                    .map(ResponseEntity::ok)
                    .orElse(ResponseEntity.notFound().build());
        }
    
        @PostMapping
        public Cliente createCliente(@RequestBody Cliente cliente) { return clienteService.save(cliente); }
    
        @PutMapping("/{id}")
        public ResponseEntity<Cliente> updateCliente(@PathVariable Long id, @RequestBody Cliente clienteDetails) {
            return ResponseEntity.ok(clienteService.update(id, clienteDetails));
        }
    
        @DeleteMapping("/{id}")
        public ResponseEntity<Void> deleteCliente(@PathVariable Long id) {
            clienteService.deleteById(id);
            return ResponseEntity.noContent().build();
        }
    }

  8. Implementação de Testes Unitários

    Crie a classe de testes `ClienteServiceTest` para validar a lógica de negócios e as operações CRUD da aplicação, assegurando que tudo funcione corretamente.

    ClienteServiceTest.java
    package com.example.apiagil.service;
    
    import com.example.apiagil.model.Cliente;
    import com.example.apiagil.repository.ClienteRepository;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
    
    import static org.junit.jupiter.api.Assertions.*;
    import static org.mockito.Mockito.*;
    
    public class ClienteServiceTest {
        @InjectMocks
        private ClienteService clienteService;
    
        @Mock
        private ClienteRepository clienteRepository;
    
        @BeforeEach
        public void init() {
            MockitoAnnotations.openMocks(this);
        }
    
        @Test
        public void testFindAll() {
            Cliente cliente1 = new Cliente();
            cliente1.setId(1L);
            cliente1.setNome("Cliente 1");
            cliente1.setEmail("cliente1@example.com");
    
            Cliente cliente2 = new Cliente();
            cliente2.setId(2L);
            cliente2.setNome("Cliente 2");
            cliente2.setEmail("cliente2@example.com");
    
            when(clienteRepository.findAll()).thenReturn(Arrays.asList(cliente1, cliente2));
    
            List<Cliente> clientes = clienteService.findAll();
            assertEquals(2, clientes.size());
            verify(clienteRepository, times(1)).findAll();
        }
    
        // Adicione mais testes unitários para métodos como findById, save, update e deleteById
    }

  9. Executando a Aplicação e Testes

    Utilize o Maven para compilar e executar a aplicação. Utilize ferramentas como Postman ou cURL para testar os endpoints da API e validar se tudo funciona conforme esperado.

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

    curl_examples
    # Listar todos os clientes
    curl -X GET http://localhost:8080/api/clientes
    # Criar um novo cliente
    curl -X POST -H "Content-Type: application/json" -d '{"nome":"Novo Cliente", "email":"novo@cliente.com", "dataNascimento":"2000-01-01"}' http://localhost:8080/api/clientes

Conclusão

Neste tutorial, você explorou como desenvolver uma API RESTful ágil usando Java e o Spring Framework, desde a configuração do ambiente até a implementação de testes unitários com JUnit. Com cada passo, foi demonstrado como estruturar sua aplicação e garantir sua funcionalidade através de testes. Com esses conceitos, você está preparado para enfrentar desafios de desenvolvimento em suas próprias aplicações com maior eficiência e qualidade, aplicando práticas ágeis que favorecem a adaptação e evolução contínua do software.

Hashtags

#Java #SpringBoot #DesenvolvimentoÁgil #JUnit #TestesAutomatizados #APIREST