Introdução: Por que testar é a base do código Java de qualidade
Todo desenvolvedor Java já passou pela situação em que, após corrigir um bug, surgem três novos. Para quebrar esse ciclo vicioso, existe o teste. Não é apenas uma marcação em uma lista de verificação, mas uma disciplina profissional que economiza nervos, dinheiro e a reputação do produto.
Neste artigo, vamos analisar os três níveis principais de teste: unit (unitário), integration (integração) e e2e (end-to-end). Vamos examiná-los com exemplos práticos em Java, usando frameworks populares: JUnit 5, Mockito, Spring Boot Test e Selenium. Você aprenderá como construir a pirâmide de testes correta e não sobrecarregar o projeto com testes inúteis.
1. Teste Unitário: Verificando unidades atômicas de código
Testes unitários são o nível mais baixo e mais rápido. Eles verificam o funcionamento de uma única "unidade" (geralmente um método ou classe) isolada de dependências externas (BD, API, sistema de arquivos).
1.1 Ferramentas e frameworks
- JUnit 5 — o padrão de facto para escrever testes em Java.
- Mockito — biblioteca para criar mocks e stubs.
- AssertJ — biblioteca para verificações (asserções) fluidas.
1.2 Exemplo de Teste Unitário com JUnit 5 e Mockito
Vamos imaginar que temos um serviço OrderService, que depende de um repositório PaymentRepository. Queremos testar a lógica do serviço sem chamar o banco de dados real.
// Classe a ser testadapublic class OrderService { private final PaymentRepository paymentRepository;
public OrderService(PaymentRepository paymentRepository) { this.paymentRepository = paymentRepository; }
public String processOrder(Order order) { if (order.getAmount() <= 0) { throw new IllegalArgumentException("O valor do pedido deve ser positivo"); } boolean success = paymentRepository.pay(order); return success ? "Pagamento bem-sucedido" : "Erro no pagamento"; }}
// Teste Unitárioimport org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension;import static org.junit.jupiter.api.Assertions.*;import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)class OrderServiceTest {
@Mock private PaymentRepository paymentRepository;
@InjectMocks private OrderService orderService;
@Test void testProcessOrder_Success() { // Arrange Order order = new Order(100.0); when(paymentRepository.pay(order)).thenReturn(true);
// Act String result = orderService.processOrder(order);
// Assert assertEquals("Pagamento bem-sucedido", result); verify(paymentRepository, times(1)).pay(order); }
@Test void testProcessOrder_NegativeAmount() { // Arrange Order order = new Order(-10.0);
// Act & Assert assertThrows(IllegalArgumentException.class, () -> { orderService.processOrder(order); }); }}Best Practices para Testes Unitários:
- Um teste — uma verificação (princípio FIRST: Fast, Isolated, Repeatable, Self-validating, Timely).
- Use nomes claros:
shouldReturnSuccess_whenPaymentIsApproved. - Não teste métodos private — teste o contrato público.
- Cubra casos de borda: null, strings vazias, números negativos.
2. Teste de Integração: Verificando a interação entre componentes
Testes de integração verificam como diferentes módulos do sistema