Era segunda-feira de manha, build verde no CI, ninguem tocou nos testes em semanas. Voce atualiza a versao do Spring Boot no pom.xml, empurra o commit e vai buscar um cafe. Quando volta: 47 testes falhando. ClassNotFoundException: org.junit.platform.runner.JUnitPlatform. A pipeline quebrando antes mesmo de chegar no deploy. Aqui esta a causa raiz, e ela nao esta no seu codigo de negocio.
O JUnit 6 foi lancado em setembro de 2025 e o Spring Boot 4 ja assume JUnit 6 como padrao. Quem esta atualizando o ecossistema Spring sem preparar a suite de testes esta tomando um choque que nao estava no changelog. Nesse artigo voce vai entender exatamente o que mudou, o que vai quebrar e como fazer a migracao sem drama.
O que é o JUnit 6 e por que ele existe
Antes de mergulhar no que quebra, vale um minuto de contexto. O JUnit 5 foi uma revolução quando saiu em 2017. Ele separou a plataforma (Launcher + Engine) do modelo de programação (junit-jupiter-api) e criou compatibilidade retroativa com JUnit 4 via Vintage Engine. Foi um design inteligente que permitiu migração gradual em projetos grandes.
O JUnit 6 é a "limpeza" dessa transição. O objetivo declarado do projeto é remover tudo que foi mantido por compatibilidade durante os anos de adoção do JUnit 5. A filosofia é: quem ainda tem JUnit 4 teve tempo suficiente para migrar. Bora limpar a casa.
Isso significa que algumas coisas que funcionavam silenciosamente no JUnit 5 simplesmente deixam de existir no 6. E é aqui que a maioria dos projetos Spring Boot vai sentir o impacto.
Os 4 breaking changes que vão quebrar seu projeto
Quando eu estava liderando a migração de um sistema legado com 8 anos de testes JUnit 4 acumulados, aprendi que a dor real não vem das mudanças anunciadas, mas das dependências transitivas que ninguém lembra que existem. O JUnit 6 tem exatamente esse perfil. Veja o que realmente importa:
1. Java 17 como baseline mínimo obrigatório
O JUnit 6 exige Java 17+. Não é uma recomendação, é uma verificação de runtime. Se seu projeto ainda está em Java 11, você não vai conseguir nem carregar a dependência.
No mercado hoje, a maioria das empresas já está em Java 17 ou migrando para Java 21/25 LTS. Mas existem projetos corporativos que ficaram parados em Java 11 por questão de estabilidade. Para esses, a migração para JUnit 6 precisa ser coordenada com a atualização da JDK.
// Verificação rápida: qual Java seu projeto usa? No pom.xml:
<properties>
<java.version>17</java.version> // precisa ser 17 ou maior
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
2. junit-platform-runner removido completamente
O módulo junit-platform-runner era um adapter que permitia rodar testes da JUnit Platform dentro do runner do JUnit 4 (com @RunWith(JUnitPlatform.class)). Ele foi removido no JUnit 6.
O erro em runtime é exatamente o que aparece na abertura deste artigo: ClassNotFoundException: org.junit.platform.runner.JUnitPlatform. E o problema vem muitas vezes via dependência transitiva — você nem sabia que estava usando.
// ANTES (JUnit 5) — ainda funciona com Vintage Engine
@RunWith(JUnitPlatform.class) // EXPLODE no JUnit 6 com ClassNotFoundException
public class MinhaTesteSuite { }
// DEPOIS (JUnit 6) — Suite API moderna, sem @RunWith
@Suite
@SelectClasses({MeuTeste1.class, MeuTeste2.class})
public class MinhaTesteSuite { }
3. JUnit Vintage Engine não é mais incluído por padrão
No JUnit 5, o junit-vintage-engine vinha junto com o spring-boot-starter-test e executava seus testes JUnit 4 automaticamente. No JUnit 6, ele foi deprecado e removido da dependência padrão.
Essa é a armadilha mais perigosa: se você ainda tem testes com @RunWith(MockitoJUnitRunner.class) ou qualquer teste com anotações org.junit.Test (não org.junit.jupiter.api.Test), eles simplesmente param de ser executados. Sem erro explícito. Sem falha visível. Eles são ignorados silenciosamente — seu CI fica verde porque os testes não falharam. Eles simplesmente sumiram da contagem.
// Teste JUnit 4 que vai sumir silenciosamente no JUnit 6 sem Vintage Engine:
import org.junit.Test; // JUnit 4 — nao mais executado sem config extra
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PedidoServiceTesteAntigo {
@Test
public void deveCalcularTotalCorretamente() {
// Este teste vai desaparecer — CI verde, cobertura real zero
}
}
<!-- pom.xml: adicionar se ainda tem testes JUnit 4 no projeto -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
Mas essa é uma solução temporária. O Vintage Engine está deprecado — o caminho certo é migrar para JUnit Jupiter.
4. @CsvSource com parsing diferente
Um breaking change menor mas que pega muita gente de surpresa: a forma como o @CsvSource trata valores nulos mudou. Antes, uma string vazia era tratada como null em alguns contextos. Agora é preciso ser explícito:
// ANTES (JUnit 5): virgula sem valor podia ser interpretada como null
@ParameterizedTest
@CsvSource({", resultado_esperado"})
// DEPOIS (JUnit 6): usar nullValues explicitamente
@ParameterizedTest
@CsvSource(value = {"NULL, resultado_esperado"}, nullValues = {"NULL"})
void testeComNull(String entrada, String esperado) { }
O checklist de migração — do diagnóstico ao deploy
Migração de testes não é glamorosa, mas é uma das habilidades que mais diferenciam um dev sênior de um dev que só resolve bugs. Aqui está o processo que funciona para projetos de médio e grande porte:
Passo 1: Diagnóstico (15 minutos)
# Encontrar todos os imports do JUnit 4 no projeto
grep -r "import org.junit.Test" src/test/ --include="*.java" | wc -l
grep -r "@RunWith" src/test/ --include="*.java" | wc -l
grep -r "junit-platform-runner" pom.xml
Se os dois primeiros retornarem 0, você não tem testes JUnit 4. Se retornarem números altos, planeje a migração antes de atualizar as dependências.
Passo 2: Atualizar dependências
<!-- Spring Boot 4 ja traz JUnit 6 via spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SE ainda tem testes JUnit 4 — transitorio, migrar depois -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
Passo 3: Migrar @RunWith para @ExtendWith
// ANTES (JUnit 4 com Spring)
@RunWith(SpringRunner.class)
public class PedidoServiceTest {
@Autowired
private PedidoService pedidoService;
@Test
public void deveRetornarPedidoAtivo() { }
}
// DEPOIS (JUnit 6 com Spring Boot)
@SpringBootTest // ja inclui @ExtendWith(SpringExtension.class)
class PedidoServiceTest { // sem public — JUnit 6 nao exige
@Autowired
private PedidoService pedidoService;
@Test
void deveRetornarPedidoAtivo() { } // sem public — mais limpo
}
Passo 4: Mapeamento completo de anotações
// JUnit 4 → JUnit 6 (Jupiter) — mapeamento de anotacoes
@Before → @BeforeEach
@After → @AfterEach
@BeforeClass → @BeforeAll // metodo deve ser static
@AfterClass → @AfterAll // ou @TestInstance(PER_CLASS)
@Ignore → @Disabled
@Test(expected = X.class) → assertThrows(X.class, () -> { ... })
@Test(timeout = 1000) → @Timeout(1)
O impacto no dia-a-dia
Depois da migração feita, a experiência de desenvolvimento com JUnit 6 é notavelmente melhor. O tempo de startup da suíte de testes cai porque a JVM não carrega mais o Vintage Engine por padrão. A API do Jupiter ficou mais limpa — menos métodos deprecated poluindo o autocomplete da IDE.
O que piora temporariamente: se você tem centenas de testes JUnit 4, o esforço é real. Mas o OpenRewrite consegue automatizar 80-90% das transformações mecânicas. Veja também a documentação oficial de migração do JUnit para casos mais complexos.
Para novos projetos Spring Boot 4 que começam do zero com JUnit 6, a experiência é limpa e consistente. As extensões do Jupiter são bem mais poderosas que os runners do JUnit 4 — @ExtendWith é composável de formas que @RunWith nunca foi.
FAQ — Perguntas frequentes
Meu projeto usa Spring Boot 3.x, preciso me preocupar com JUnit 6 agora?
Não imediatamente. O Spring Boot 3.x continua usando JUnit 5 (Jupiter). O JUnit 6 entra como padrão no Spring Boot 4. Se você está planejando migrar para Spring Boot 4 no futuro, é um bom momento para começar a limpar os testes JUnit 4 do seu projeto — a migração vai ser um pré-requisito obrigatório.
Posso usar JUnit 6 e JUnit 4 no mesmo projeto ao mesmo tempo?
Sim, com o junit-vintage-engine adicionado explicitamente. Os dois modelos convivem sem conflito via JUnit Platform, com relatórios unificados. Mas esta é uma solução de transição — o Vintage Engine está deprecado e será descontinuado em versões futuras.
O Mockito funciona normalmente com JUnit 6?
Sim. O MockitoExtension do Mockito 5+ é compatível com JUnit Jupiter. A forma de uso muda: @RunWith(MockitoJUnitRunner.class) vira @ExtendWith(MockitoExtension.class). O Mockito 5.23.0 (lançado em março de 2026) inclui suporte explícito para JUnit 6. Veja mais sobre testes com TDD com Java e o ciclo Red-Green-Refactor.
Como saber se meus testes JUnit 4 foram ignorados silenciosamente?
Compare a contagem de testes executados antes e depois da atualização. No Maven: mvn test mostra Tests run: X. Se esse número caiu significativamente sem falhas, você tem testes JUnit 4 sendo ignorados. Adicione o junit-vintage-engine temporariamente para confirmar.
Existe ferramenta para migrar testes automaticamente?
Sim. O OpenRewrite tem a receita org.openrewrite.java.testing.junit5.JUnit4to5Migration que resolve 80-90% dos casos mecânicos. Use como ponto de partida, revise o diff antes de commitar. Para projetos Spring Boot, leia também sobre Spring Boot 4: breaking changes e guia de migração.
Conclusão: prepare a migração antes de precisar
- O JUnit 6 é um breaking change real para projetos com testes JUnit 4 legados
- O junit-platform-runner foi removido e causa
ClassNotFoundExceptionem runtime - O Vintage Engine não é mais incluído por padrão: testes JUnit 4 somem silenciosamente e o CI fica verde
- OpenRewrite automatiza 80-90% das transformações mecânicas — use como ponto de partida
- Quem começa do zero com Spring Boot 4 e JUnit 6 tem uma experiência limpa, sem legado
Você já iniciou a migração para JUnit 6 no seu projeto? Qual foi o breaking change mais doloroso que você encontrou? Conta nos comentários — projetos legados com anos de testes JUnit 4 têm histórias muito boas (e muito dolorosas).
Na próxima semana vou mostrar como usar o TestContainers com Spring Boot para testar com banco de dados real, e por que ele pega bugs que o H2 esconde. Veja também o artigo sobre TDD com Java: o ciclo Red-Green-Refactor na prática.