01. Introdução ao Kotest Framework

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

  • Saberemos como configurar e executar especificações do Kotest 6 no JUnit Platform usando Gradle 9 e Maven.
  • Compreenderemos a estrutura de uma classe de especificação do Kotest (DescribeSpec, BehaviorSpec, ShouldSpec), seus blocos de ciclo de vida e a organização semântica Given / When / Then.
  • Saberemos como escrever asserções expressivas com kotest-assertions-core (shouldBe, coleções, exceções e assertSoftly) e testes orientados a dados (data-driven testing) com withData.

Como Configurar o Ambiente para Kotest 6 e BDD em Kotlin

O Kotest 6 é o framework moderno de testes e especificações BDD mais expressivo e flexível do ecossistema Kotlin. Diferente de soluções legadas que dependiam de linguagens dinâmicas adicionais (como Groovy com Spock) ou anotações verbosas do JUnit 4, o Kotest foi projetado nativamente para tirar proveito da sintaxe idiomática do Kotlin: lambdas com receptor (type-safe builders), funções infix, corrotinas assíncronas e forte tipagem estática.

Neste guia prático, aprenderemos a configurar um ambiente completo e reprodutível do zero, integrando Spring Boot, Kotlin 2.4+, Gradle 9 (Kotlin DSL), Kotest 6.2.6 e MockK 1.14.0.


#### 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!

Passo 1: Criando o Projeto Base via CLI (start.spring.io)

O meio mais rápido e reprodutível de criar um projeto moderno em Kotlin com Gradle é através da API de linha de comando do Spring Initializr via curl.

Execute o comando abaixo no terminal para gerar a estrutura base do projeto com Gradle Kotlin DSL (build.gradle.kts), Kotlin e Java 21:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
curl https://start.spring.io/starter.tgz \
  -d type=gradle-project-kotlin \
  -d language=kotlin \
  -d javaVersion=21 \
  -d groupId=com.testwithspring.master \
  -d artifactId=kotest-spring-demo \
  -d name=kotest-spring-demo \
  -d description="Projeto de introdução ao Kotest 6 e BDD com Spring" \
  -d packageName=com.testwithspring.master \
  -d dependencies=web \
  -d baseDir=kotest-spring-demo | tar -xzvf -

cd kotest-spring-demo

[!NOTE] Explicação dos parâmetros utilizados:

  • type=gradle-project-kotlin: Gera o projeto com Gradle configurado nativamente com Kotlin DSL (build.gradle.kts).
  • language=kotlin: Configura o compilador kotlin-jvm e a biblioteca padrão kotlin-stdlib.
  • javaVersion=21: Seleciona o JDK 21 LTS (Long-Term Support).
  • dependencies=web: Adiciona o spring-boot-starter-web para termos componentes Spring gerenciados.
  • baseDir=kotest-spring-demo | tar -xzvf -: Descompacta os arquivos diretamente no diretório kotest-spring-demo.

Passo 2: Configurando Dependências no build.gradle.kts

Abra o arquivo build.gradle.kts na raiz do projeto. Precisamos adicionar as bibliotecas essenciais para escrever e executar testes BDD com o Kotest 6 e criar mocks idiomáticos com o MockK:

  1. io.kotest:kotest-runner-junit5:6.2.6: Implementa a TestEngine do Kotest sobre a JUnit Platform. É esse runner que descobre e executa as especificações Kotest quando o Gradle executa a tarefa test.
  2. io.kotest:kotest-assertions-core:6.2.6: Oferece o conjunto de asserções idiomáticas e expressivas do Kotlin (shouldBe, shouldNotBe, shouldThrow, assertSoftly, etc.).
  3. io.kotest.extensions:kotest-extensions-spring:1.3.0: Permite a integração nativa entre o ciclo de vida do Kotest e o TestContextManager do Spring Framework, possibilitando injeção de dependências no construtor da especificação e anotações como @SpringBootTest.
  4. io.mockk:mockk:1.14.0: Biblioteca de mocks criada especificamente para Kotlin, com suporte pleno a corrotinas, lambdas e classes finais (final classes por padrão no Kotlin).

Edite a seção dependencies do seu build.gradle.kts conforme o exemplo abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
val kotestVersion = "6.2.6"
val kotestSpringExtensionVersion = "1.3.0"
val mockkVersion = "1.14.0"

dependencies {
    // Dependências da aplicação Spring Boot
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    // Starter de testes do Spring Boot
    testImplementation("org.springframework.boot:spring-boot-starter-test")

    // Kotest 6: Runner JUnit 5 e Asserções Core
    testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
    testImplementation("io.kotest:kotest-assertions-core:$kotestVersion")

    // Extensão Spring para Kotest
    testImplementation("io.kotest.extensions:kotest-extensions-spring:$kotestSpringExtensionVersion")

    // MockK: Mocks idiomáticos para Kotlin
    testImplementation("io.mockk:mockk:$mockkVersion")
}

[!TIP] Caso prefira gerenciar versões via BOM (Bill of Materials), você pode importar testImplementation(platform("io.kotest:kotest-bom:6.2.6")) e omitir as versões dos módulos kotest-runner-junit5 e kotest-assertions-core.


Passo 3: Configurando a Tarefa tasks.withType<Test>

O Kotest 6 executa em cima da JUnit Platform. Para que o Gradle reconheça a engine do Kotest e forneça logs detalhados e hierárquicos das especificações BDD, configure a tarefa tasks.withType<Test> no seu build.gradle.kts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tasks.withType<Test> {
    // 1. Obrigatório: Instrui o Gradle a utilizar o JUnit Platform para descobrir a engine do Kotest
    useJUnitPlatform()

    // 2. Formatação detalhada de logs no terminal
    testLogging {
        events("passed", "skipped", "failed")
        showExceptions = true
        showCauses = true
        showStackTraces = true
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
    }

    // 3. Parâmetros recomendados para o Kotest Runner
    systemProperty("kotest.framework.dump.config", "false")
    systemProperty("kotest.framework.parallelism", "1") // Defina > 1 para execução concorrente
}

[!IMPORTANT] A diretiva useJUnitPlatform() é estritamente obrigatória. Sem ela, o Gradle tentará rodar a suíte com o runner vintage do JUnit 3/4 e ignorará completamente as classes de especificação do Kotest.


Passo 4: Criando a Primeira Especificação BDD com DescribeSpec

Com o ambiente configurado, vamos criar uma especificação BDD utilizando o estilo DescribeSpec, um dos mais populares e legíveis do Kotest.

Primeiro, crie uma classe de serviço simples em src/main/kotlin/com/testwithspring/master/GreetingService.kt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.testwithspring.master

import org.springframework.stereotype.Service

@Service
class GreetingService {

    fun buildGreeting(name: String?): String {
        val trimmed = name?.trim().orEmpty()
        return if (trimmed.isBlank()) {
            "Olá, Visitante!"
        } else {
            "Olá, $trimmed!"
        }
    }
}

Agora crie o arquivo de teste correspondente em src/test/kotlin/com/testwithspring/master/GreetingServiceSpec.kt:

 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
package com.testwithspring.master

import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldEndWith
import io.kotest.matchers.string.shouldStartWith

class GreetingServiceSpec : DescribeSpec({

    val greetingService = GreetingService()

    describe("GreetingService - geração de saudações") {

        describe("quando um nome válido é fornecido") {
            it("deve retornar saudação personalizada com o nome") {
                val resultado = greetingService.buildGreeting("Maria")
                resultado shouldBe "Olá, Maria!"
            }

            it("deve remover espaços em branco no início e no final do nome") {
                val resultado = greetingService.buildGreeting("   Carlos   ")
                resultado shouldBe "Olá, Carlos!"
            }

            it("deve iniciar a mensagem com o prefixo 'Olá,'") {
                val resultado = greetingService.buildGreeting("João")
                resultado shouldStartWith "Olá,"
                resultado shouldEndWith "João!"
            }
        }

        describe("quando o nome não é fornecido ou contém apenas espaços") {
            it("deve retornar mensagem para visitante quando o argumento for null") {
                val resultado = greetingService.buildGreeting(null)
                resultado shouldBe "Olá, Visitante!"
            }

            it("deve retornar mensagem para visitante quando a string for vazia") {
                val resultado = greetingService.buildGreeting("")
                resultado shouldBe "Olá, Visitante!"
            }

            it("deve retornar mensagem para visitante quando a string contiver apenas espaços") {
                val resultado = greetingService.buildGreeting("    ")
                resultado shouldBe "Olá, Visitante!"
            }
        }
    }
})

[!TIP] Se a sua especificação precisar injetar componentes gerenciados pelo Spring (como @Autowired ou via construtor), basta registrar a extensão extension(SpringExtension) no construtor do DescribeSpec e anotar sua classe com @SpringBootTest:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import io.kotest.extensions.spring.SpringExtension
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
class GreetingServiceIntegrationSpec(
    private val greetingService: GreetingService
) : DescribeSpec({
    extension(SpringExtension)

    describe("Integração com contexto Spring") {
        it("deve injetar o serviço com sucesso") {
            greetingService.buildGreeting("Spring") shouldBe "Olá, Spring!"
        }
    }
})

Passo 5: Executando e Validando a Suíte com Gradle

Para executar sua suíte e validar as especificações, execute o comando a seguir no terminal:

1
./gradlew test

Com o bloco testLogging configurado no Gradle, a árvore hierárquica BDD será impressa diretamente no console:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> Task :test

GreetingServiceSpec > GreetingService - geração de saudações > quando um nome válido é fornecido > deve retornar saudação personalizada com o nome PASSED
GreetingServiceSpec > GreetingService - geração de saudações > quando um nome válido é fornecido > deve remover espaços em branco no início e no final do nome PASSED
GreetingServiceSpec > GreetingService - geração de saudações > quando um nome válido é fornecido > deve iniciar a mensagem com o prefixo 'Olá,' PASSED
GreetingServiceSpec > GreetingService - geração de saudações > quando o nome não é fornecido ou contém apenas espaços > deve retornar mensagem para visitante quando o argumento for null PASSED
GreetingServiceSpec > GreetingService - geração de saudações > quando o nome não é fornecido ou contém apenas espaços > deve retornar mensagem para visitante quando a string for vazia PASSED
GreetingServiceSpec > GreetingService - geração de saudações > quando o nome não é fornecido ou contém apenas espaços > deve retornar mensagem para visitante quando a string contiver apenas espaços PASSED

BUILD SUCCESSFUL in 1s

Para executar apenas a sua especificação individualmente:

1
./gradlew test --tests "com.testwithspring.master.GreetingServiceSpec"

O Gradle também gera um relatório HTML visual com todo o detalhamento da execução no diretório build/reports/tests/test/index.html.

Pronto! Seu ambiente está configurado para explorar todas as potencialidades do Kotest 6 nos próximos tópicos e aulas práticas.

Conteúdo