JUnit 6 Spring Boot Breaking Changes Migracao - Meu Universo Nerd

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 ClassNotFoundException em 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.