Pular para o conteúdo principal

Capítulo 1: Introdução aos Testes

Introdução​

Antes de escrever seu primeiro teste, é crucial entender por que e como testar de forma eficaz. Este capítulo estabelece as bases para uma estratégia de testes profissional.


🎯 Objetivos do Capítulo​

Ao final deste capítulo, você saberá:

  1. ✅ Por que os testes são essenciais
  2. ✅ Distinguir os diferentes tipos de testes
  3. ✅ Compreender a pirâmide de testes
  4. ✅ Configurar seu projeto para testes
  5. ✅ Conhecer as boas práticas

1. Por que Testar?​

O Custo dos Bugs​

Momento da DetecçãoCusto RelativoImpacto
Durante o desenvolvimento1xNenhum
Em fase de teste10xAtraso
Em produção100xCliente impactado, urgência

Benefícios dos Testes​

✅ Confiança: O código faz o que deveria fazer
✅ Regressão: Detecta bugs introduzidos por mudanças
✅ Documentação: Os testes explicam o comportamento esperado
✅ Design: Escrever testes força um bom design (SOLID)
✅ Refatoração: Modificar o código sem medo
✅ Deploy: CI/CD automatizado

Sem Testes vs Com Testes​

// Sem testes 😰
public void deployToProduction() {
// "Espero que funcione..."
// "Vamos ver em produção..."
// "Dedos cruzados..."
}

// Com testes 😎
public void deployToProduction() {
// ✅ 347 testes passaram
// ✅ Cobertura: 85%
// ✅ Todos os quality gates passaram
// → Deploy com confiança!
}

2. Tipos de Testes​

Visão Geral​

Testes Unitários​

O quê: Testar uma unidade de código isoladamente (método, classe)

Características:

  • Rápidos (milissegundos)
  • Isolados (sem banco de dados, sem rede)
  • Numerosos (maioria dos testes)
@Test
void shouldCalculatePriceWithDiscount() {
// Testa SOMENTE a lógica de cálculo
PriceCalculator calculator = new PriceCalculator();

double result = calculator.applyDiscount(100.0, 20);

assertEquals(80.0, result);
}

Testes de Integração​

O quê: Testar vários componentes juntos

Características:

  • Mais lentos (segundos)
  • Usam recursos reais (banco de dados, etc.)
  • Menos numerosos
@SpringBootTest
class OrderServiceIntegrationTest {

@Autowired
private OrderService orderService;

@Test
void shouldSaveOrderToDatabase() {
// Testa o service + repository + banco de dados
Order order = orderService.createOrder(/*...*/);

assertNotNull(order.getId());
}
}

Testes End-to-End (E2E)​

O quê: Testar a aplicação completa como um usuário

Características:

  • Lentos (minutos)
  • Ambiente completo
  • Poucos testes
// Com Selenium, por exemplo
@Test
void userShouldBeAbleToCompleteCheckout() {
browser.navigate("/products");
browser.click("#add-to-cart");
browser.click("#checkout");
browser.fill("#address", "123 Main St");
browser.click("#confirm");

assertThat(browser.getText("#confirmation"))
.contains("Order confirmed");
}

3. A Pirâmide de Testes​

Estrutura Ideal​

                    /\
/ \
/ E2E\ Poucos, lentos, frágeis
/ \
/────────\
/Integração\ Moderado
/ \
/────────────────\
/ Testes Unitários \ Muitos, rápidos, estáveis
/____________________\

Proporções Recomendadas​

TipoProporçãoTempo de Execução
Unitários70%~5 segundos
Integração20%~30 segundos
E2E10%~5 minutos

Anti-Pattern: O Sorvete de Casquinha​

            ____________________
/ Muitos E2E \ ❌ Lentos, frágeis
/────────────────────────\
/ Pouca integração \
/────────────────────────────\
/ Poucos testes \
/ unitários \
/──────────────────────────────────\

❌ RUIM: Testes lentos, manutenção difícil

4. TDD e BDD​

TDD (Test-Driven Development)​

Escrever os testes ANTES do código:

// Etapa 1: RED - O teste falha (o método não existe)
@Test
void shouldReturnEmptyListWhenNoProducts() {
ProductService service = new ProductService();
List<Product> products = service.findAll();
assertTrue(products.isEmpty());
}

// Etapa 2: GREEN - Implemente o mínimo
public List<Product> findAll() {
return new ArrayList<>();
}

// Etapa 3: REFACTOR - Melhore se necessário

BDD (Behavior-Driven Development)​

Testes que descrevem o comportamento em linguagem natural:

// Formato Given-When-Then
@Test
void givenNewUser_whenRegister_thenAccountIsCreated() {
// Given (Contexto)
RegistrationRequest request = new RegistrationRequest(
"john", "john@email.com", "password123"
);

// When (Ação)
User user = userService.register(request);

// Then (Resultado esperado)
assertNotNull(user.getId());
assertEquals("john", user.getUsername());
assertTrue(user.isEnabled());
}

5. Configuração Maven​

Dependências de Teste​

<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Test (inclui JUnit 5, Mockito, AssertJ) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- H2 para testes -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>

<!-- Security Test (opcional) -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

O que está incluído no spring-boot-starter-test​

spring-boot-starter-test inclui:
├── JUnit 5 (Jupiter)
├── Mockito
├── AssertJ
├── Hamcrest
├── JSONPath
├── JsonAssert
└── Spring Test

Estrutura dos Arquivos de Teste​

src/
├── main/
│ └── java/
│ └── com/example/
│ ├── service/
│ │ └── ProductService.java
│ └── repository/
│ └── ProductRepository.java
└── test/
└── java/
└── com/example/
├── service/
│ └── ProductServiceTest.java ← Testes unitários
├── repository/
│ └── ProductRepositoryTest.java ← Testes de integração
└── controller/
└── ProductControllerTest.java ← Testes de API

6. Configuração de Teste​

application-test.properties​

# src/test/resources/application-test.properties

# Banco de dados H2 em memória
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create-drop

# Desativar logs verbosos
logging.level.org.hibernate.SQL=WARN
spring.jpa.show-sql=false

# Desativar Flyway para testes (opcional)
spring.flyway.enabled=false

Ativar o Perfil de Teste​

@SpringBootTest
@ActiveProfiles("test") // ← Usa application-test.properties
class MyIntegrationTest {
// ...
}

7. Executar os Testes​

Via Maven​

# Todos os testes
mvn test

# Testes com relatório
mvn test jacoco:report

# Um teste específico
mvn test -Dtest=ProductServiceTest

# Um método específico
mvn test -Dtest=ProductServiceTest#shouldFindProductById

# Skip nos testes (evite!)
mvn install -DskipTests

Via IDE​

IntelliJ IDEA:
- Clique direito na classe/método → Run Test
- Ctrl+Shift+F10 (Windows)
- Ctrl+Shift+R (Mac)

VS Code:
- Ícone "Run Test" acima do @Test
- Test Explorer na barra lateral

8. Boas Práticas​

Naming Convention​

// Formato recomendado: should_ExpectedBehavior_When_StateUnderTest
@Test
void should_ReturnEmpty_When_NoProductsExist() { }

// Ou: given_When_Then
@Test
void given_ValidProduct_When_Save_Then_IdIsGenerated() { }

// Evite:
@Test
void test1() { } // ❌ Não descritivo
@Test
void testProductService() { } // ❌ Muito vago

Padrão AAA (Arrange-Act-Assert)​

@Test
void shouldCalculateTotalPrice() {
// Arrange (Preparar)
ShoppingCart cart = new ShoppingCart();
cart.addItem(new Item("Laptop", 1000.0));
cart.addItem(new Item("Mouse", 50.0));

// Act (Executar)
double total = cart.calculateTotal();

// Assert (Verificar)
assertEquals(1050.0, total);
}

Um Teste = Um Conceito​

// ❌ RUIM: Testa várias coisas
@Test
void testProduct() {
Product p = new Product("Test", 100.0);
assertEquals("Test", p.getName());
assertEquals(100.0, p.getPrice());
p.applyDiscount(10);
assertEquals(90.0, p.getPrice());
assertThrows(Exception.class, () -> p.applyDiscount(-5));
}

// ✅ BOM: Um teste por comportamento
@Test
void shouldCreateProductWithNameAndPrice() {
Product p = new Product("Test", 100.0);
assertEquals("Test", p.getName());
assertEquals(100.0, p.getPrice());
}

@Test
void shouldApplyDiscountCorrectly() {
Product p = new Product("Test", 100.0);
p.applyDiscount(10);
assertEquals(90.0, p.getPrice());
}

@Test
void shouldRejectNegativeDiscount() {
Product p = new Product("Test", 100.0);
assertThrows(IllegalArgumentException.class,
() -> p.applyDiscount(-5));
}

9. Resumo​

Pontos-Chave​

ConceitoDescrição
Teste UnitárioTesta uma unidade isoladamente
Teste IntegraçãoTesta vários componentes juntos
Teste E2ETesta a aplicação completa
TDDTestes escritos antes do código
AAAArrange-Act-Assert

Pirâmide de Testes​

10% E2E (lentos, frágeis)
20% Integração (moderados)
70% Unitários (rápidos, numerosos)

Checklist​

✅ spring-boot-starter-test no pom.xml
✅ application-test.properties configurado
✅ Testes em src/test/java
✅ Nomes descritivos
✅ Um teste = um conceito
✅ Padrão AAA

🎯 Próximo Passo​

Hora da prática! Vamos explorar o JUnit 5 em detalhes.

➡️ Capítulo 2: Testes Unitários com JUnit 5


⬅️ Voltar ao Módulo 9