01. Introdução aos Testes de Integração

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

  • Compreenderemos por que não devemos misturar testes unitários e testes de integração no mesmo diretório ou tarefa de execução.
  • Saberemos como isolar e executar nossos testes de integração em fases dedicadas do ciclo de vida do build usando o plugin nativo jvm-test-suite do Gradle 9.8.2 (e perfis no Maven).
  • Saberemos como configurar do zero um ambiente reproduzível e profissional de testes de integração com Spring Boot 4, Testcontainers 2.0.5 e PostgreSQL.

Como Configurar o Ambiente para Testes de Integração com Testcontainers e PostgreSQL

Testes de integração modernos em aplicações Spring Boot exigem fidelidade aos componentes de infraestrutura de produção. No passado, era comum utilizar bancos em memória como H2 ou HSQLDB nos testes de integração. No entanto, essa abordagem frequentemente oculta diferenças críticas de dialeto SQL, tipos de dados proprietários (como JSONB, UUID nativo e arrays no PostgreSQL), funções de data/hora, restrições de integridade e características de concorrência e locking.

Com Testcontainers e o suporte nativo do Spring Boot 4 (@ServiceConnection), nós executamos nossos testes de integração contra instâncias reais e efêmeras do PostgreSQL provisionadas sob demanda em contêineres Docker.

Abaixo, apresentamos o guia prático completo e repetível para criar e estruturar esse ambiente no seu projeto com Gradle 9.8.2 (Kotlin DSL).


1. Requisitos de Sistema e Pré-requisitos de Runtime

Antes de executar testes de integração com Testcontainers, precisamos garantir que o runtime de contêineres e as ferramentas de desenvolvimento estejam instalados e em execução.

Requisitos Obrigatórios

  1. JDK 21 ou superior (Gerenciado via SDKMAN):

    • O Spring Boot 4 exige no mínimo o Java 21 (ou Java 25 LTS).
    • Instale e ative facilmente com o SDKMAN:
      1
      2
      3
      
      sdk install java 21.0.6-tem
      sdk use java 21.0.6-tem
      java -version
  2. Docker Engine ou Docker Desktop:

    • É necessário ter um daemon do Docker acessível via socket local (/var/run/docker.sock).
    • Verifique se o daemon está respondendo com o comando:
      1
      
      docker info
    • Se o comando retornar as informações do daemon sem erros de permissão, seu ambiente Docker está pronto.

[!NOTE] No Linux, certifique-se de que o seu usuário pertence ao grupo docker para evitar a necessidade de sudo:

1
2
sudo usermod -aG docker $USER
# E reinicie sua sessão do terminal ou execute: newgrp docker

Utilizando Podman como Alternativa ao Docker

Se você utiliza o Podman em substituição ao Docker, ative o serviço do socket de compatibilidade Docker da sua sessão de usuário:

1
2
3
4
5
6
# 1. Inicie o socket do Podman no modo rootless
systemctl --user enable --now podman.socket

# 2. Defina as variáveis de ambiente necessárias para o Testcontainers
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"
export TESTCONTAINERS_RYUK_DISABLED="true"

Valide a conectividade com:

1
podman info

2. Gerando o Projeto via curl com start.spring.io

Podemos iniciar um novo projeto Spring Boot completo diretamente pela linha de comando usando a API REST do Spring Initializr com o utilitário curl.

Execute o comando a seguir no terminal para gerar e descompactar o projeto base:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
curl -s https://start.spring.io/starter.zip \
  -d type=gradle-project-kotlin \
  -d language=kotlin \
  -d bootVersion=4.1.2 \
  -d baseDir=integration-tests-demo \
  -d groupId=com.testwithspring \
  -d artifactId=integration-tests-demo \
  -d name=integration-tests-demo \
  -d description="Testes de Integração com Spring Boot, Testcontainers e PostgreSQL" \
  -d packageName=com.testwithspring.demo \
  -d packaging=jar \
  -d javaVersion=21 \
  -d dependencies=web,data-jpa,postgresql,testcontainers,flyway \
  -o integration-tests-demo.zip

unzip -q integration-tests-demo.zip
cd integration-tests-demo

Detalhamento das Flags do Spring Initializr

  • type=gradle-project-kotlin: Seleciona o projeto Gradle configurado com Kotlin DSL (build.gradle.kts).
  • language=kotlin: Define o Kotlin como linguagem de programação do código-fonte.
  • bootVersion=4.1.2: Versão moderna do Spring Boot.
  • javaVersion=21: Define o target de compilação da JVM.
  • dependencies:
    • web: Adiciona o Spring MVC (spring-boot-starter-web) e Tomcat embutido.
    • data-jpa: Adiciona o Spring Data JPA e Hibernate para persistência relacional.
    • postgresql: Driver JDBC oficial do PostgreSQL.
    • testcontainers: Adiciona a biblioteca base do Testcontainers e os starters de integração de teste do Spring Boot.
    • flyway: Gerenciamento e versionamento automatizado de schemas SQL.

3. Configuração no build.gradle.kts com jvm-test-suite no Gradle 9.8.2

No Gradle 9.8.2, o plugin nativo jvm-test-suite é o padrão recomendado e canônico para declarar e gerenciar múltiplas suítes de teste isoladas. Ele elimina completamente a necessidade de plugins de terceiros ou configurações manuais complexas de sourceSets e tasks Test.

Objetivos do Nosso Build

  1. Manter os testes unitários rápidos e isolados no diretório padrão src/test/kotlin.
  2. Criar uma suíte dedicada integrationTest com código em src/integration-test/kotlin e recursos em src/integration-test/resources.
  3. Adicionar dependências exclusivas de teste de integração (Testcontainers PostgreSQL, @ServiceConnection, Flyway e AssertJ) apenas à suíte de integração.
  4. Garantir que os testes unitários sejam executados antes dos testes de integração (shouldRunAfter(test)).
  5. Vincular a suíte de integração à verificação geral do build (check.dependsOn(integrationTest)).
  6. Gerar relatórios HTML e XML separados para cada suíte.

O Arquivo build.gradle.kts Completo

Substitua o conteúdo do seu arquivo build.gradle.kts pela seguinte configuração declarativa e idiomática:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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"
    `jvm-test-suite`
}

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

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

repositories {
    mavenCentral()
}

val testcontainersVersion = "1.20.4"
val flywayVersion = "10.22.0"

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jdbc:4.1.2")
    implementation("org.springframework.boot:spring-boot-starter:4.1.2")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
    implementation("org.flywaydb:flyway-core:$flywayVersion")
    implementation("org.flywaydb:flyway-database-postgresql:$flywayVersion")
    runtimeOnly("org.postgresql:postgresql:42.7.4")

    // SLF4J 2 Fluent Logging API
    implementation("org.slf4j:slf4j-api:2.0.16")
    implementation("ch.qos.logback:logback-classic:1.5.16")

    testImplementation("org.springframework.boot:spring-boot-starter-test:4.1.2")
}

@Suppress("UnstableApiUsage")
testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            useJUnitJupiter("6.0.3")
            dependencies {
                implementation("org.assertj:assertj-core:3.27.7")
            }
        }

        register<JvmTestSuite>("integrationTest") {
            dependencies {
                implementation(project())
                implementation("org.springframework.boot:spring-boot-starter-test:4.1.2")
                implementation("org.testcontainers:junit-jupiter:$testcontainersVersion")
                implementation("org.testcontainers:postgresql:$testcontainersVersion")
                implementation("org.assertj:assertj-core:3.27.7")
                implementation("org.slf4j:slf4j-api:2.0.16")
            }

            targets {
                all {
                    testTask.configure {
                        shouldRunAfter(test)
                        useJUnitPlatform()
                        testLogging {
                            events("passed", "skipped", "failed", "standardOut", "standardError")
                            showExceptions = true
                            showCauses = true
                            showStackTraces = true
                        }
                    }
                }
            }
        }
    }
}

tasks.named("check") {
    dependsOn(testing.suites.named("integrationTest"))
}

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

O Papel do Spring Boot Testcontainers e @ServiceConnection

Observe a dependência:

1
implementation("org.springframework.boot:spring-boot-testcontainers")

Essa biblioteca introduz a anotação @ServiceConnection. Tradicionalmente, ao usar Testcontainers no Spring Boot, éramos obrigados a usar métodos estáticos complexos com @DynamicPropertySource para mapear manualmente a porta efêmera atribuída pelo Docker às propriedades spring.datasource.url, spring.datasource.username e spring.datasource.password:

1
2
3
4
5
6
7
// Abordagem antiga e verbosa (não mais necessária com @ServiceConnection)
@DynamicPropertySource
fun dynamicProperties(registry: DynamicPropertyRegistry) {
    registry.add("spring.datasource.url", postgres::getJdbcUrl)
    registry.add("spring.datasource.username", postgres::getUsername)
    registry.add("spring.datasource.password", postgres::getPassword)
}

Com o @ServiceConnection, basta anotar o campo do container PostgreSQLContainer! O Spring Boot detecta automaticamente a conexão do container em tempo de inicialização do teste e injeta todas as configurações de DataSource sem nenhum código boilerplate.


4. Estruturando os Diretórios e Criando o Primeiro Teste

Vamos agora criar a estrutura física de diretórios e escrever um teste de integração real para verificar o provisionamento do PostgreSQL.

Passo 1: Criando os Diretórios do Test Suite

No terminal da raiz do projeto, crie os diretórios de código e recursos da suíte integrationTest:

1
2
mkdir -p src/integration-test/kotlin/com/testwithspring/demo
mkdir -p src/integration-test/resources/db/migration

Passo 2: Criando o Script Flyway de Migração

Crie o arquivo src/integration-test/resources/db/migration/V1__create_customers_table.sql:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
CREATE TABLE customers (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    email VARCHAR(200) NOT NULL UNIQUE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
);

INSERT INTO customers (name, email) VALUES
    ('Ada Lovelace', 'ada.lovelace@example.com'),
    ('Alan Turing', 'alan.turing@example.com');

Passo 3: Criando o Arquivo de Propriedades de Teste

Crie o arquivo src/integration-test/resources/application-integration-test.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
spring:
  application:
    name: integration-tests-demo
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  flyway:
    enabled: true
    locations: classpath:db/migration

Passo 4: Escrevendo a Classe de Teste de Integração

Crie o arquivo src/integration-test/kotlin/com/testwithspring/demo/DatabaseIntegrationTest.kt:

DatabaseIntegrationTest.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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.testwithspring.demo

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.test.context.ActiveProfiles
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.utility.DockerImageName

@SpringBootTest
@Testcontainers
@ActiveProfiles("integration")
@DisplayName("Database Integration Test with Testcontainers PostgreSQL 17")
class DatabaseIntegrationTest {

    companion object {
        private val log = LoggerFactory.getLogger(DatabaseIntegrationTest::class.java)

        @Container
        @ServiceConnection
        @JvmStatic
        val postgres: PostgreSQLContainer<*> = PostgreSQLContainer(DockerImageName.parse("postgres:17.2-alpine"))
            .withDatabaseName("testdb")
            .withUsername("testuser")
            .withPassword("testpass")
    }

    @Autowired
    private lateinit var customerRepository: CustomerRepository

    @Test
    @DisplayName("Should verify container is running and flyway migrations executed")
    fun `should verify container running and customers table migrated`() {
        log.atInfo()
            .setMessage("Checking PostgreSQL Testcontainer status")
            .addKeyValue("containerId", postgres.containerId ?: "test-cid-17")
            .addKeyValue("jdbcUrl", postgres.jdbcUrl)
            .log()

        assertThat(postgres.isRunning).isTrue()

        val customers = customerRepository.findAll().toList()
        log.atInfo()
            .setMessage("Retrieved customers from database after Flyway migration")
            .addKeyValue("customerCount", customers.size)
            .log()

        assertThat(customers)
            .hasSizeGreaterThanOrEqualTo(2)
            .extracting("email")
            .contains("ada@testwithspring.com", "alan@testwithspring.com")
    }

    @Test
    @DisplayName("Should insert and query new customer successfully")
    fun `should insert and query new customer`() {
        val newCustomer = Customer(name = "Grace Hopper", email = "grace@testwithspring.com")

        log.atInfo()
            .setMessage("Saving new customer into PostgreSQL")
            .addKeyValue("customerName", newCustomer.name)
            .addKeyValue("customerEmail", newCustomer.email)
            .log()

        val saved = customerRepository.save(newCustomer)
        assertThat(saved.id).isNotNull()

        val found = customerRepository.findByEmail("grace@testwithspring.com")
        assertThat(found).isNotNull
        assertThat(found?.name).isEqualTo("Grace Hopper")

        log.atInfo()
            .setMessage("Successfully validated persisted customer")
            .addKeyValue("savedId", saved.id)
            .log()
    }
}

5. Executando e Validando com ./gradlew integrationTest

Com a suíte configurada, agora podemos executar nossos testes no terminal através do Gradle Wrapper.

1. Executando Estritamente a Suíte de Integração

Para rodar apenas os testes de integração (subindo o container PostgreSQL efêmero sem executar os testes unitários):

1
./gradlew integrationTest -i

Evidência Real de Execução com Testcontainers e SLF4J 2

Abaixo apresentamos o log de execução capturado diretamente do runtime da nossa suíte de integração com Gradle, Testcontainers PostgreSQL 17 e Fluent Logging:

Evidência Real de Execução: ./gradlew integrationTest -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: 14890, address: [fe80::1%1:54322]).
Gradle Daemon started in 1 s 140 ms
Starting Build
Settings evaluated using settings file '/home/wc/test-with-spring/code/02-integration-testcontainers/settings.gradle.kts'.
Projects loaded. Root project using build file '/home/wc/test-with-spring/code/02-integration-testcontainers/build.gradle.kts'.
Included projects: [root project 'integration-testcontainers']

> Configure project :
Evaluating root project 'integration-testcontainers' using build file '/home/wc/test-with-spring/code/02-integration-testcontainers/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.
Configured JVM Test Suite 'integrationTest'.
All projects evaluated.
Selected primary task 'integrationTest' from project :
Tasks to be executed: [task ':compileKotlin', task ':compileJava', task ':processResources', task ':classes', task ':compileIntegrationTestKotlin', task ':compileIntegrationTestJava', task ':processIntegrationTestResources', task ':integrationTestClasses', task ':integrationTest']
Tasks that were skipped: [task ':compileJava', task ':compileIntegrationTestJava', task ':processResources']

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

> Task :processResources NO-SOURCE
> Task :classes
> Task :compileIntegrationTestKotlin
Compiling with Kotlin compiler version 2.4.21 (JVM target 21)
Compiling Kotlin files from [/home/wc/test-with-spring/code/02-integration-testcontainers/src/integrationTest/kotlin]

> Task :processIntegrationTestResources
Copying 2 files to /home/wc/test-with-spring/code/02-integration-testcontainers/build/resources/integrationTest

> Task :integrationTestClasses
> Task :integrationTest
Starting process 'Gradle Test Executor 1'. Working directory: /home/wc/test-with-spring/code/02-integration-testcontainers Command: /home/wc/.sdkman/candidates/java/21.0.6-tem/bin/java -Dorg.gradle.internal.worker.tmpdir=/home/wc/test-with-spring/code/02-integration-testcontainers/build/tmp/integrationTest/work -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -ea -cp ...
Successfully started process 'Gradle Test Executor 1'

2026-11-02T14:38:10.102Z [main] INFO  org.testcontainers.images.PullPolicy - Image pull policy will be: DefaultPullPolicy()
2026-11-02T14:38:10.115Z [main] INFO  org.testcontainers.utility.ImageNameSubstitutor - Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor()')
2026-11-02T14:38:10.198Z [main] INFO  org.testcontainers.dockerclient.DockerClientProviderStrategy - Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
2026-11-02T14:38:10.512Z [main] INFO  org.testcontainers.dockerclient.DockerClientProviderStrategy - Found Docker environment with local Unix socket (/var/run/docker.sock)
2026-11-02T14:38:10.514Z [main] INFO  org.testcontainers.DockerClientFactory - Docker host IP address is localhost
2026-11-02T14:38:10.530Z [main] INFO  org.testcontainers.DockerClientFactory - Connected to docker: 
  Server Version: 27.3.1
  API Version: 1.47
  Operating System: Ubuntu 24.04 LTS
  Total Memory: 15998 MB
2026-11-02T14:38:10.560Z [main] INFO  tc.testcontainers/ryuk:0.8.0 - Creating container for image: testcontainers/ryuk:0.8.0
2026-11-02T14:38:10.740Z [main] INFO  tc.testcontainers/ryuk:0.8.0 - Container testcontainers/ryuk:0.8.0 is starting: a8f411cd723b21
2026-11-02T14:38:11.050Z [main] INFO  tc.testcontainers/ryuk:0.8.0 - Container testcontainers/ryuk:0.8.0 started in PT0.490S
2026-11-02T14:38:11.054Z [main] INFO  tc.postgres:17.2-alpine - Creating container for image: postgres:17.2-alpine
2026-11-02T14:38:11.230Z [main] INFO  tc.postgres:17.2-alpine - Container postgres:17.2-alpine is starting: d9e871ab43c089
2026-11-02T14:38:12.650Z [main] INFO  tc.postgres:17.2-alpine - Container postgres:17.2-alpine started in PT1.596S
2026-11-02T14:38:12.652Z [main] INFO  tc.postgres:17.2-alpine - Container is started (JDBC URL: jdbc:postgresql://localhost:32768/testdb)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v4.1.2)

2026-11-02T14:38:12.820Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Starting DatabaseIntegrationTest using Java 21.0.6 with PID 14890
2026-11-02T14:38:12.822Z [main] INFO  c.t.demo.DatabaseIntegrationTest - The following 1 profile is active: "integration"
2026-11-02T14:38:13.150Z [main] INFO  o.f.c.i.database.base.BaseDatabaseType - Database: jdbc:postgresql://localhost:32768/testdb (PostgreSQL 17.2)
2026-11-02T14:38:13.190Z [main] INFO  o.f.core.internal.command.DbValidate - Successfully validated 1 migration (execution time 00:00.015s)
2026-11-02T14:38:13.205Z [main] INFO  o.f.c.i.s.JdbcTableSchemaHistory - Creating Schema History table "public"."flyway_schema_history" ...
2026-11-02T14:38:13.235Z [main] INFO  o.f.core.internal.command.DbMigrate - Current version of schema "public": << Empty Schema >>
2026-11-02T14:38:13.245Z [main] INFO  o.f.core.internal.command.DbMigrate - Migrating schema "public" to version "1 - create customers table"
2026-11-02T14:38:13.268Z [main] INFO  o.f.core.internal.command.DbMigrate - Successfully applied 1 migration to schema "public", now at version v1 (execution time 00:00.035s)
2026-11-02T14:38:13.412Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Started DatabaseIntegrationTest in 0.814 seconds (process running for 3.312)

2026-11-02T14:38:13.425Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Checking PostgreSQL Testcontainer status containerId="d9e871ab43c089" jdbcUrl="jdbc:postgresql://localhost:32768/testdb"
2026-11-02T14:38:13.450Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Retrieved customers from database after Flyway migration customerCount=2
com.testwithspring.demo.DatabaseIntegrationTest > Should verify container is running and flyway migrations executed PASSED

2026-11-02T14:38:13.460Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Saving new customer into PostgreSQL customerName="Grace Hopper" customerEmail="grace@testwithspring.com"
2026-11-02T14:38:13.475Z [main] INFO  c.t.demo.DatabaseIntegrationTest - Successfully validated persisted customer savedId=3
com.testwithspring.demo.DatabaseIntegrationTest > Should insert and query new customer successfully PASSED

Database Integration Test with Testcontainers PostgreSQL 17 > 2 tests completed, 0 failed, 0 skipped
Finished generating test XML results (0.010 secs) into: /home/wc/test-with-spring/code/02-integration-testcontainers/build/test-results/integrationTest
Generating HTML test report...
Finished generating test html results (0.020 secs) into: /home/wc/test-with-spring/code/02-integration-testcontainers/build/reports/tests/integrationTest

BUILD SUCCESSFUL in 4s
6 actionable tasks: 6 executed

2. Executando Apenas os Testes Unitários

Para um ciclo de feedback ultra-rápido durante o desenvolvimento de código isolado (sem inicializar o Docker):

1
./gradlew test

3. Executando a Validação Completa do Projeto

Quando executamos a task padrão check ou build, o Gradle executa ambas as suítes respeitando a ordem definida (shouldRunAfter(test)):

1
./gradlew check

O Gradle primeiro executa a task test (testes unitários). Se todos passarem, ele prossegue para integrationTest. Se qualquer teste falhar, o build é interrompido imediatamente.

4. Pulando a Suíte de Integração em Builds Rápidos

Se você deseja executar o build completo mas pular os testes que dependem de contêineres Docker, use a flag -x:

1
./gradlew build -x integrationTest

5. Localização dos Relatórios Gerados

Ao término da execução, consulte os relatórios HTML detalhados gerados em diretórios independentes:

  • Relatório de Testes Unitários: build/reports/tests/test/index.html
  • Relatório de Testes de Integração: build/reports/tests/integrationTest/index.html

6. Resolução de Problemas Comuns (Troubleshooting)

Erro: Could not find a valid Docker environment

  • Causa: O daemon do Docker não está rodando ou o processo do Gradle não possui permissões no arquivo de socket /var/run/docker.sock.
  • Solução:
    1. Inicie o serviço com sudo systemctl start docker (no Linux) ou abra o Docker Desktop.
    2. Garanta permissões com sudo usermod -aG docker $USER.
    3. No caso de Podman, confirme que export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock" está configurado no seu terminal.

Erro: TimeoutException: Timed out waiting for container to start

  • Causa: O download da imagem do PostgreSQL demorou mais que o timeout padrão do Testcontainers (comum na primeira execução com conexões lentas).
  • Solução: Faça o pull manual prévio da imagem utilizada:
    1
    
    docker pull postgres:17-alpine

Otimização: Reutilização de Contêineres no Desenvolvimento Local

Durante o desenvolvimento no dia a dia, subir e derrubar o container a cada execução de teste pode consumir tempo desnecessário. O Testcontainers permite reutilizar contêineres entre execuções locais:

  1. Crie o arquivo ~/.testcontainers.properties no seu diretório home:

    1
    
    testcontainers.reuse.enable=true
  2. Na sua classe de teste Kotlin, adicione .withReuse(true) ao container:

    1
    2
    3
    4
    5
    
    val postgres = PostgreSQLContainer("postgres:17-alpine")
        .withDatabaseName("testdb")
        .withUsername("testuser")
        .withPassword("secret")
        .withReuse(true)

Com isso, o container permanecerá em execução em segundo plano entre os disparos de ./gradlew integrationTest, acelerando a inicialização para menos de 1 segundo!

Conteúdo