gradle-testing-setup

Configures comprehensive testing in Gradle including JUnit 5, TestContainers, test separation (unit vs integration), and code coverage with JaCoCo. Use when asked to "set up JUnit 5", "configure TestContainers", "separate integration tests", or "add code coverage". Works with build.gradle.kts, test source sets, and CI/CD configurations.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "gradle-testing-setup" with this command: npx skills add dawiddutoit/custom-claude/dawiddutoit-custom-claude-gradle-testing-setup

Gradle Testing Setup

Table of Contents

When to Use This Skill

Use this skill when you need to:

  • Set up JUnit 5 (Jupiter) testing framework
  • Configure TestContainers for integration tests with real databases
  • Separate unit tests from integration tests
  • Measure code coverage with JaCoCo
  • Enforce minimum code coverage thresholds
  • Configure parallel test execution for faster test runs
  • Set up test logging and reporting in CI/CD
  • Create separate source sets for integration tests

Quick Start

Add to build.gradle.kts:

dependencies {
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.junit.jupiter:junit-jupiter")
    testImplementation("org.testcontainers:testcontainers:1.21.0")
    testImplementation("org.testcontainers:junit-jupiter:1.21.0")
    testImplementation("org.testcontainers:postgresql:1.21.0")

    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

plugins {
    id("jacoco")
}

tasks.test {
    useJUnitPlatform()
}

tasks.jacocoTestReport {
    dependsOn(tasks.test)
    finalizedBy(tasks.jacocoTestCoverageVerification)
}

tasks.check {
    dependsOn(tasks.jacocoTestReport)
}

Run tests:

./gradlew test                    # Run unit tests
./gradlew test jacocoTestReport   # With coverage report
./gradlew integrationTest         # Run integration tests (if configured)

Instructions

Step 1: Configure JUnit 5 (Jupiter)

Add JUnit 5 dependencies:

dependencies {
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.11.0")
}

tasks.test {
    useJUnitPlatform()
}

Advanced JUnit 5 configuration:

tasks.test {
    useJUnitPlatform {
        // Include/exclude tags
        includeTags("unit", "integration")
        excludeTags("slow", "manual")

        // Include/exclude by engine
        includeEngines("junit-jupiter")
        excludeEngines("junit-vintage")
    }

    // Filter tests by pattern
    filter {
        includeTestsMatching("*Test")
        includeTestsMatching("*Tests")
        excludeTestsMatching("*IntegrationTest")
    }

    // Parallel test execution
    maxParallelForks = Runtime.getRuntime().availableProcessors() / 2

    // System properties for tests
    systemProperty("junit.jupiter.execution.parallel.enabled", "true")
    systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent")

    // Detailed logging
    testLogging {
        events("passed", "skipped", "failed", "standardOut")
        showExceptions = true
        showStackTraces = true
        showCauses = true
        exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
    }
}

Step 2: Set Up TestContainers for Integration Tests

Add TestContainers dependencies:

dependencies {
    testImplementation("org.testcontainers:testcontainers:1.21.0")
    testImplementation("org.testcontainers:junit-jupiter:1.21.0")
    testImplementation("org.testcontainers:postgresql:1.21.0")
    testImplementation("org.testcontainers:gcloud:1.21.0")  // For Pub/Sub emulator
}

tasks.test {
    useJUnitPlatform()

    // Docker socket configuration
    systemProperty("testcontainers.reuse.enable", "true")
}

Example integration test with TestContainers:

@SpringBootTest
@Testcontainers
class SupplierChargesIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");

    @Container
    static GenericContainer<?> pubsub = new GenericContainer<>("google/cloud-sdk:emulators")
        .withExposedPorts(8085)
        .withCommand("gcloud", "beta", "emulators", "pubsub", "start", "--host-port=0.0.0.0:8085");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
        registry.add("spring.cloud.gcp.pubsub.emulator-host",
            () -> "localhost:" + pubsub.getMappedPort(8085));
    }

    @Test
    void testDatabaseAndPubSub() {
        // Test with real PostgreSQL and Pub/Sub emulator
    }
}

Step 3: Separate Unit and Integration Tests

Create separate source sets and tasks:

sourceSets {
    create("integrationTest") {
        java {
            srcDir("src/integrationTest/java")
            compileClasspath += sourceSets.main.get().output + sourceSets.test.get().output
            runtimeClasspath += sourceSets.main.get().output + sourceSets.test.get().output
        }
        resources {
            srcDir("src/integrationTest/resources")
        }
    }
}

// Create integration test task
val integrationTest = tasks.register<Test>("integrationTest") {
    description = "Run integration tests"
    group = "verification"

    testClassesDirs = sourceSets["integrationTest"].output.classesDirs
    classpath = sourceSets["integrationTest"].runtimeClasspath

    useJUnitPlatform()

    // Run after unit tests
    shouldRunAfter(tasks.test)

    // Logging
    testLogging {
        events("passed", "skipped", "failed")
    }
}

// Include integration tests in overall check
tasks.check {
    dependsOn(integrationTest)
}

Directory structure:

src/
├── main/
│   └── java/
├── test/                          # Unit tests
│   ├── java/
│   │   └── com/example/
│   │       ├── ServiceTest.java
│   │       └── ControllerTest.java
│   └── resources/
│       └── application-test.yml
└── integrationTest/               # Integration tests
    ├── java/
    │   └── com/example/
    │       └── ServiceIntegrationTest.java
    └── resources/
        └── application-integration.yml

Step 4: Configure Code Coverage with JaCoCo

Add JaCoCo plugin and configuration:

plugins {
    id("jacoco")
}

jacoco {
    toolVersion = "0.8.12"
}

tasks.jacocoTestReport {
    dependsOn(tasks.test)

    reports {
        xml.required = true
        html.required = true
        csv.required = false

        xml.outputLocation = layout.buildDirectory.file("reports/jacoco/test/jacocoTestReport.xml")
        html.outputLocation = layout.buildDirectory.dir("reports/jacoco/test/html")
    }

    finalizedBy(tasks.jacocoTestCoverageVerification)
}

// Enforce coverage minimums
tasks.jacocoTestCoverageVerification {
    violationRules {
        // Overall coverage requirement
        rule {
            element = "BUNDLE"
            limit {
                minimum = BigDecimal("0.60")  // 60% minimum
            }
        }

        // Class-level requirements
        rule {
            element = "CLASS"
            excludes = listOf("**/config/*", "**/dto/*")
            limit {
                counter = "LINE"
                value = "COVEREDRATIO"
                minimum = BigDecimal("0.50")  // 50% per class
            }
        }

        // Method-level requirements
        rule {
            element = "METHOD"
            limit {
                counter = "LINE"
                value = "COVEREDRATIO"
                minimum = BigDecimal("0.40")  // 40% per method
            }
        }
    }
}

// Generate report after tests
tasks.test {
    finalizedBy(tasks.jacocoTestReport)
}

// Include in overall check
tasks.check {
    dependsOn(tasks.jacocoTestReport)
}

Step 5: Configure Test Logging

Use prettier test output with test-logger plugin:

plugins {
    id("com.adarshr.test-logger") version "4.0.0"
}

testlogger {
    theme = "mocha"  // Options: plain, standard, mocha, standard-parallel, mocha-parallel
    showExceptions = true
    showStackTraces = true
    showFullStackTraces = false
    showCauses = true
    slowThreshold = 2000  // Warn for tests > 2 seconds
    showSummary = true
    showPassed = true
    showSkipped = true
    showFailed = true
    showStandardStreams = false
}

Step 6: Configure Docker for TestContainers

Ensure Docker is properly configured:

# Verify Docker is running
docker ps

# On macOS with Docker Desktop
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST=unix://${HOME}/.docker/run/docker.sock

# Run tests
./gradlew test

In gradle.properties for CI/CD:

org.gradle.logging.level=info
testcontainers.reuse.enable=true

Examples

Example: Running Different Test Suites

# Unit tests only
./gradlew test

# Integration tests only
./gradlew integrationTest

# All tests
./gradlew check

# With coverage report
./gradlew test jacocoTestReport

# Specific test class
./gradlew test --tests "com.example.ServiceTest"

# Tests matching pattern
./gradlew test --tests "*IntegrationTest"

# Single test method
./gradlew test --tests "com.example.ServiceTest.testMethod"

# Verbose output
./gradlew test --info

# Continuous mode (rerun on changes)
./gradlew test --continuous

For advanced examples including TestContainers integration, GitHub Actions with coverage reports, and CI coverage verification, see references/advanced-examples.md.

Commands Reference

See references/commands-and-troubleshooting.md for complete command reference and troubleshooting guide.

See Also

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

uv-python-version-management

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-best-practices-async-context-manager

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

textual-widget-development

No summary provided by upstream source.

Repository SourceNeeds Review