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
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 -versionCriaçã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>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.propertiesspring.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=trueCriaçã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.javapackage 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; } }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.javapackage 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 }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.javapackage 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); } }Implementação do Controlador `ClienteController`
Implemente a classe `ClienteController` para expor os endpoints da API, permitindo operações CRUD dos clientes.
ClienteController.javapackage 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(); } }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.javapackage 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 }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 testcurl_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.
