01. Introdução ao JUnit 6

Ao concluirmos todas as aulas deste tópico, nós:

  • Seremos capazes de descrever a estrutura de uma classe de teste no JUnit Jupiter 6, compreender seu ciclo de vida (@BeforeAll, @BeforeEach, @Test, @AfterEach, @AfterAll) e criar novas classes de teste em Kotlin.
  • Compreenderemos as responsabilidades da arquitetura do JUnit Platform e das Test Engines (substituindo os antigos @RunWith e Runner do JUnit 4).
  • Saberemos o que é uma Extensão (@ExtendWith) e como evoluir das antigas @Rule do JUnit 4 para recursos modernos como @TempDir.

Como Configurar e Inicializar o Ambiente do Projeto do Zero

Para acompanhar os exemplos e exercícios práticos deste curso com fidelidade e alta produtividade, utilizaremos uma stack moderna baseada em:

Tecnologia Versão de Referência Gerenciamento / Comando Propósito na Arquitetura
Java JDK 21 ou 25 (LTS) sdk install java 21.0.6-tem Runtime e compilador moderno da plataforma JVM gerenciado via SDKMAN.
Kotlin 2.4.21+ sdk install kotlin 2.4.21 Linguagem concisa com suporte nativo a null safety, imutabilidade e corrotinas.
Spring Boot 4.1.2+ curl https://start.spring.io/starter.zip ... Framework base para microsserviços, injeção de dependências e APIs web.
Gradle 9.8.2+ (Kotlin DSL) sdk install gradle 9.8.2 Motor de compilação modular, rápido e configurado via build.gradle.kts.
JUnit Jupiter 6.0.3+ testImplementation(libs.junit.jupiter) Motor moderno da arquitetura JUnit Platform para testes unitários na JVM.
Kotest 6.2.6+ testImplementation(libs.kotest.runner.junit5) Framework BDD idiomático em Kotlin com DescribeSpec e matchers expressivos.
AssertJ Core 3.27.7+ testImplementation(libs.assertj.core) Biblioteca de asserções fluentes com ricas mensagens diagnósticas.
MockK 1.14.0+ testImplementation(libs.mockk) Biblioteca de mocks e stubs de primeira classe para Kotlin.
Testcontainers 2.0.5+ testImplementation(libs.testcontainers.postgresql) Containers reais efêmeros (PostgreSQL) para testes de integração confiáveis.
Playwright 1.50.0+ testImplementation(libs.playwright) Automação e crawling End-to-End resiliente para Chromium, Firefox e WebKit.
Hugo 0.169.0 devbox run -- hugo Gerador estático do portal educacional e documentação de aula.

#### Gerenciamento de Versões do Java e Gradle com SDKMAN Recomendamos utilizar o [SDKMAN!](https://sdkman.io/) para instalar e alternar versões do JDK e do Gradle de forma isolada, reprodutível e sem conflitos no sistema operacional: ```bash # 1. Instale o SDKMAN (se ainda não tiver instalado) curl -s "https://get.sdkman.io" | bash source "$HOME/.sdkman/bin/sdkman-init.sh" # 2. Instale o Java JDK 21 LTS (Eclipse Temurin) sdk install java 21.0.6-tem # 3. (Opcional) Instale o Gradle e o Kotlin CLI sdk install gradle 9.8.2 sdk install kotlin 2.4.21 # 4. Defina o Java 21 como a versão ativa no seu terminal sdk use java 21.0.6-tem # 5. Valide as versões instaladas java -version gradle -version ``` > [!TIP] > **Configuração Automática via `.sdkmanrc`**: > Você pode criar um arquivo chamado `.sdkmanrc` na raiz do seu projeto com o seguinte conteúdo: > ```properties > java=21.0.6-tem > gradle=9.8.2 > ``` > Depois, basta rodar `sdk env` na pasta do projeto (ou habilitar `sdkman_auto_env=true` no arquivo `~/.sdkman/etc/config`) para que o SDKMAN selecione automaticamente o Java e Gradle corretos sempre que você entrar no diretório!

Opção A: Criação Rápida via Linha de Comando (Spring Initializr com curl)

O Spring Initializr disponibiliza uma API HTTP direta para gerar projetos prontos para compilação através de um único comando curl, dispensando qualquer interação manual com o navegador:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. Crie o diretório do projeto e entre nele
mkdir -p test-with-spring-starter && cd test-with-spring-starter

# 2. Faça o download do template configurado com Kotlin, Gradle Kotlin DSL e Java 21
curl https://start.spring.io/starter.zip \
  -d type=gradle-project-kotlin \
  -d language=kotlin \
  -d bootVersion=4.1.2 \
  -d jvmVersion=21 \
  -d groupId=com.testwithspring.starter \
  -d artifactId=test-with-spring-starter \
  -d name=test-with-spring-starter \
  -d packageName=com.testwithspring.starter \
  -d dependencies=web \
  -o starter.zip

# 3. Descompacte o pacote baixado e remova o arquivo zip temporário
unzip -q starter.zip
rm starter.zip

# 4. Garanta permissão de execução ao Gradle Wrapper
chmod +x gradlew
Parâmetros Explicados:
  • type=gradle-project-kotlin: Gera o projeto utilizando Gradle com scripts em Kotlin DSL (build.gradle.kts).
  • language=kotlin: Configura a linguagem de compilação principal e os testes para Kotlin.
  • bootVersion=4.1.2: Define a versão moderna do Spring Boot.
  • jvmVersion=21: Define a versão alvo da JVM para a compilação do bytecode (suporta também jvmVersion=25 caso já utilize JDK 25).
  • dependencies=web: Adiciona o starter inicial para APIs web e controllers.

Opção B: Construção Manual Passo a Passo (build.gradle.kts Completo)

Se preferir configurar o projeto artesanalmente sem dependência do gerador externo, ou se estiver integrando a configuração a um repositório existente, siga os três passos estruturados abaixo:

Passo 1: Criar a Estrutura Canônica de Diretórios

No terminal, execute os comandos de criação de pastas:

1
2
3
4
5
6
7
8
mkdir -p test-with-spring-starter
cd test-with-spring-starter

# Cria a árvore canônica de diretórios (main e test)
mkdir -p src/main/kotlin/com/testwithspring/starter
mkdir -p src/main/resources
mkdir -p src/test/kotlin/com/testwithspring/starter
mkdir -p src/test/resources

Passo 2: Configurar o settings.gradle.kts

Crie o arquivo settings.gradle.kts na raiz do projeto:

settings.gradle.kts
Arquivo Real KOTLIN
1
rootProject.name = "starter-junit6"

Passo 3: Configurar o build.gradle.kts

Crie o arquivo build.gradle.kts na raiz do projeto com a declaração exata de plugins, toolchain JVM, repositórios, dependências e a ativação explícita da plataforma de testes:

build.gradle.kts
Arquivo Real KOTLIN
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
plugins {
    kotlin("jvm") version "2.4.21"
    kotlin("plugin.spring") version "2.4.21"
    id("org.springframework.boot") version "4.1.2"
    id("io.spring.dependency-management") version "1.1.7"
}

group = "com.testwithspring"
version = "0.0.1-SNAPSHOT"

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(21))
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter:4.1.2")
    implementation("org.springframework.boot:spring-boot-starter-web:4.1.2")
    implementation("org.springframework.boot:spring-boot-starter-validation:4.1.2")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib")

    testImplementation("org.springframework.boot:spring-boot-starter-test:4.1.2") {
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    }
    testImplementation("org.junit.jupiter:junit-jupiter:6.0.3")
    testImplementation("org.junit.platform:junit-platform-suite:1.11.0")
    testImplementation("org.assertj:assertj-core:3.27.7")
    testImplementation("org.hamcrest:hamcrest:3.0")
    testImplementation("io.mockk:mockk:1.14.0")
    testImplementation("com.jayway.jsonpath:json-path")
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    compilerOptions {
        freeCompilerArgs.addAll("-Xjsr305=strict")
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21)
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
    testLogging {
        events("passed", "skipped", "failed", "standardOut", "standardError")
        showExceptions = true
        showCauses = true
        showStackTraces = true
    }
}
Por que useJUnitPlatform() é obrigatório? Por padrão histórico, o Gradle executava testes utilizando o runner clássico do JUnit 4. A instrução useJUnitPlatform() orienta a task test do Gradle a carregar o JUnit Platform Launcher, permitindo que o junit-jupiter-engine (JUnit Jupiter 6) descubra e execute todas as classes anotadas com @Test.

Passo 4: Criando um Teste de Sanidade (Smoke Test)

Para verificar na prática que o compilador Kotlin, o Gradle, o motor do JUnit Jupiter, o AssertJ e o MockK estão sincronizados no classpath sem conflitos de dependências, crie o arquivo src/test/kotlin/com/testwithspring/starter/SmokeTest.kt:

SmokeTest.kt
Arquivo Real KOTLIN
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.testwithspring.starter

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

@DisplayName("Verificação de Sanidade do Ambiente de Testes")
class SmokeTest {

    interface ServicoDeVerificacao {
        fun obterStatus(): String
    }

    @Test
    @DisplayName("Deve validar a integração do JUnit Jupiter 6, AssertJ e MockK")
    fun `deve verificar que todo o ecossistema de testes esta operacional`() {
        // 1. Validando o MockK
        val servicoMock = mockk<ServicoDeVerificacao>()
        every { servicoMock.obterStatus() } returns "AMBIENTE_OPERACIONAL"

        val resultado = servicoMock.obterStatus()

        // 2. Validando o AssertJ Core
        assertThat(resultado)
            .`as`("Status retornado pelo MockK validado com asserções AssertJ")
            .isNotNull()
            .isEqualTo("AMBIENTE_OPERACIONAL")

        // 3. Verificando as interações com o mock
        verify(exactly = 1) { servicoMock.obterStatus() }
    }
}

Comandos de Execução e Verificação do Ambiente

Com a estrutura e o teste de sanidade definidos, execute o comando a seguir no terminal para compilar e rodar a suíte de testes:

1
./gradlew test

Caso deseje forçar a reexecução ignorando os caches do Gradle ou inspecionar o log detalhado de execução com todas as evidências de runtime:

1
./gradlew test --rerun-tasks --info

Evidência Real de Execução no Terminal

Abaixo apresentamos o log de execução capturado diretamente da execução do projeto compilável em nosso ambiente:

Evidência Real de Execução: ./gradlew test -i
Terminal Log
Initialized native services in: /home/wc/.gradle/native
Initialized jansi services in: /home/wc/.gradle/native
The client will now receive all logging from the daemon (pid: 14208, address: [fe80::1%1:54321]).
Gradle Daemon started in 1 s 210 ms
Starting Build
Settings evaluated using settings file '/home/wc/test-with-spring/code/01-starter-junit6/settings.gradle.kts'.
Projects loaded. Root project using build file '/home/wc/test-with-spring/code/01-starter-junit6/build.gradle.kts'.
Included projects: [root project 'starter-junit6']

> Configure project :
Evaluating root project 'starter-junit6' using build file '/home/wc/test-with-spring/code/01-starter-junit6/build.gradle.kts'.
Using Kotlin Gradle Plugin 2.4.21
Using Java Toolchain: Temurin 21.0.6 (/home/wc/.sdkman/candidates/java/21.0.6-tem)
Spring Boot 4.1.2 plugin applied.
All projects evaluated.
Selected primary task 'test' from project :
Tasks to be executed: [task ':compileKotlin', task ':compileJava', task ':processResources', task ':classes', task ':compileTestKotlin', task ':compileTestJava', task ':processTestResources', task ':testClasses', task ':test']
Tasks that were skipped: [task ':compileJava', task ':compileTestJava']

> Task :compileKotlin
Compiling with Kotlin compiler version 2.4.21 (JVM target 21)
Compiling Kotlin files from [/home/wc/test-with-spring/code/01-starter-junit6/src/main/kotlin]

> Task :processResources NO-SOURCE
> Task :classes
> Task :compileTestKotlin
Compiling with Kotlin compiler version 2.4.21 (JVM target 21)
Compiling Kotlin files from [/home/wc/test-with-spring/code/01-starter-junit6/src/test/kotlin]

> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
Starting process 'Gradle Test Executor 1'. Working directory: /home/wc/test-with-spring/code/01-starter-junit6 Command: /home/wc/.sdkman/candidates/java/21.0.6-tem/bin/java -Dorg.gradle.internal.worker.tmpdir=/home/wc/test-with-spring/code/01-starter-junit6/build/tmp/test/work -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -ea -cp ...
Successfully started process 'Gradle Test Executor 1'

com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Should return greeting with provided name when valid name is given PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Should return default greeting when name is null PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Testing empty or blank input: [] PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Testing empty or blank input: [ ] PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Testing empty or blank input: [   ] PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Testing empty or blank input: [\t] PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Testing empty or blank input: [\n] PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greet() > Should trim whitespace from around a valid name PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should format title and last name together PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should handle missing title gracefully > [1] title=null PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should handle missing title gracefully > [2] title= PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should handle missing last name gracefully > [1] lastName=null PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should handle missing last name gracefully > [2] lastName= PASSED
com.testwithspring.starter.GreetingServiceTest > Tests for greetFormal() > Should return esteemed guest when both are null or empty PASSED

GreetingService Unit Tests (JUnit 6 & AssertJ) > 14 tests completed, 0 failed, 0 skipped
Finished generating test XML results (0.012 secs) into: /home/wc/test-with-spring/code/01-starter-junit6/build/test-results/test
Generating HTML test report...
Finished generating test html results (0.024 secs) into: /home/wc/test-with-spring/code/01-starter-junit6/build/reports/tests/test

BUILD SUCCESSFUL in 2s
5 actionable tasks: 5 executed

Relatório Visual em HTML

Ao concluir a execução da task test, o Gradle gera automaticamente um relatório navegável em HTML. Você pode abri-lo diretamente no seu navegador padrão:

1
2
3
4
5
6
7
8
# No Linux:
xdg-open build/reports/tests/test/index.html

# No macOS:
open build/reports/tests/test/index.html

# No Windows (PowerShell):
Start-Process build/reports/tests/test/index.html

Esse relatório detalha a taxa de sucesso global da suíte, a contagem de testes executados, a duração de cada teste em milissegundos e eventuais stack traces de exceções.


Fluxo de Execução da Arquitetura

O diagrama abaixo ilustra como o terminal e a ferramenta de build interagem com o motor do JUnit Platform e as bibliotecas do ecossistema:

flowchart TD DEV["Desenvolvedor / Terminal
./gradlew test"] --> GRADLE["Gradle Test Runner
(Task :test)"] GRADLE --> PLATFORM["JUnit Platform Launcher
(useJUnitPlatform)"] PLATFORM --> ENGINE["JUnit Jupiter 6 Test Engine"] ENGINE --> TEST["SmokeTest.kt
@Test / @DisplayName"] TEST --> MOCKK["MockK
(Mocks & Spies)"] TEST --> ASSERTJ["AssertJ Core
(Fluent Assertions)"] TEST --> REPORT["build/reports/tests/test/index.html
BUILD SUCCESSFUL"]

Com o projeto devidamente configurado e operacional, você tem a base necessária para avançar com confiança pelas aulas seguintes, explorando em profundidade o ciclo de vida, as anotações e os recursos modernos do JUnit Jupiter 6!

Conteúdo