Testes em Java: de testes unitários a testes de ponta a ponta
Testes de qualidade são a base de um software confiável. No mundo Java, existe uma pirâmide de testes bem estabelecida que inclui três níveis principais: testes unitários, de integração e de ponta a ponta (e2e). Neste artigo, analisaremos cada um deles, forneceremos exemplos práticos em Java e explicaremos como construir corretamente uma estratégia de testes no seu projeto.
1. Testes unitários: a base da pirâmide
Testes unitários verificam componentes individuais do programa de forma isolada — geralmente uma classe ou método. Eles são executados rapidamente, não exigem dependências externas (BD, API) e devem cobrir a maior parte da lógica de negócios.
Ferramentas para testes unitários em Java
- JUnit 5 — framework padrão para escrever testes.
- Mockito — biblioteca para criar objetos mock.
- AssertJ — asserções fluidas para verificações legíveis.
Exemplo de teste unitário com JUnit 5 e Mockito
Suponha que temos um serviço OrderService que depende de PaymentGateway. Queremos testar a lógica de cálculo de desconto sem chamar o gateway de pagamento real.
// Classe para testepublic class OrderService { private final PaymentGateway paymentGateway;
public OrderService(PaymentGateway paymentGateway) { this.paymentGateway = paymentGateway; }
public double calculateDiscount(double amount) { if (amount > 1000) { return amount * 0.1; // 10% de desconto } return 0; }}
// 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.assertEquals;
@ExtendWith(MockitoExtension.class)public class OrderServiceTest {
@Mock private PaymentGateway paymentGateway;
@InjectMocks private OrderService orderService;
@Test public void testCalculateDiscount_AmountAbove1000_Returns10Percent() { double result = orderService.calculateDiscount(1500); assertEquals(150.0, result, 0.001); }
@Test public void testCalculateDiscount_AmountBelow1000_ReturnsZero() { double result = orderService.calculateDiscount(500); assertEquals(0.0, result); }}Melhores práticas: use @BeforeEach para inicializar dados comuns, evite testar métodos privados diretamente, siga a regra "uma verificação por teste".
2. Testes de integração: verificando a interação
Testes de integração verificam como vários módulos funcionam juntos — por exemplo, a interação de um serviço com o banco de dados, filas de mensagens ou APIs externas. Ao contrário dos testes unitários, aqui são usadas dependências reais ou in-memory.
Ferramentas para testes de integração
- Spring Boot Test — para testar aplicações Spring.
- Testcontainers — para levantar contêineres Docker (BD, Kafka).
- H2 Database — banco in-memory para testes rápidos.
Exemplo de teste de integração com Spring Boot
Testando um controlador REST com conexão real ao banco de dados (no teste usamos H2).
// Controlador@RestController@RequestMapping("/api/users")public class UserController { private final UserRepository userRepository;
public UserController(UserRepository userRepository) { this.userRepository = userRepository; }
@GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id) .orElseThrow(() -> new Runti