
gradle-testing-setup
by dawiddutoit
Collection of Claude Code skills, agents, and plugins
SKILL.md
name: gradle-testing-setup description: | Configure comprehensive testing in Gradle including JUnit 5, TestContainers, test separation (unit vs integration), and code coverage with JaCoCo. Use when setting up test frameworks, configuring integration tests, measuring code coverage, or establishing test quality gates.
Gradle Testing Setup
Table of Contents
Purpose
Configure professional-grade testing infrastructure for Gradle projects with proper test isolation, containerized dependencies via TestContainers, code coverage tracking, and test quality gates. This skill covers JUnit 5 setup, integration test separation, and coverage enforcement.
When to Use
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 1: Complete Testing Configuration
// build.gradle.kts
plugins {
id("java")
id("org.springframework.boot") version "3.5.5"
id("jacoco")
id("com.adarshr.test-logger") version "4.0.0"
}
// === DEPENDENCIES ===
dependencies {
// Unit testing
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
// Integration testing with containers
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")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.11.0")
}
// === SOURCE SETS ===
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")
}
}
}
// === UNIT TESTS ===
tasks.test {
useJUnitPlatform {
excludeTags("integration", "slow")
}
filter {
excludeTestsMatching("*IntegrationTest")
}
maxParallelForks = Runtime.getRuntime().availableProcessors() / 2
testLogging {
events("passed", "skipped", "failed")
showExceptions = true
showStackTraces = true
}
finalizedBy(tasks.jacocoTestReport)
}
// === INTEGRATION TESTS ===
val integrationTest = tasks.register<Test>("integrationTest") {
description = "Run integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
useJUnitPlatform {
includeTags("integration")
}
shouldRunAfter(tasks.test)
systemProperty("testcontainers.reuse.enable", "true")
testLogging {
events("passed", "skipped", "failed")
showExceptions = true
}
}
// === CODE COVERAGE ===
jacoco {
toolVersion = "0.8.12"
}
tasks.jacocoTestReport {
dependsOn(tasks.test)
reports {
xml.required = true
html.required = true
}
finalizedBy(tasks.jacocoTestCoverageVerification)
}
tasks.jacocoTestCoverageVerification {
violationRules {
rule {
element = "BUNDLE"
limit {
minimum = BigDecimal("0.60")
}
}
rule {
element = "CLASS"
excludes = listOf("**/config/*", "**/dto/*")
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = BigDecimal("0.50")
}
}
}
}
// === TEST LOGGER ===
testlogger {
theme = "mocha"
showExceptions = true
slowThreshold = 2000
}
// === INCLUDE IN OVERALL CHECK ===
tasks.check {
dependsOn(integrationTest)
dependsOn(tasks.jacocoTestReport)
}
Example 2: 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
Example 3: TestContainers Integration Test
// src/integrationTest/java/com/example/SupplierServiceIntegrationTest.java
package com.example;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Testcontainers
class SupplierServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Autowired
private SupplierService supplierService;
@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);
}
@Test
void testCreateSupplier() {
Supplier supplier = supplierService.create("New Supplier");
assertThat(supplier).isNotNull();
assertThat(supplier.getId()).isPositive();
assertThat(supplier.getName()).isEqualTo("New Supplier");
}
}
Example 4: GitHub Actions with Coverage Reports
# .github/workflows/test.yml
name: Tests and Coverage
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Run unit tests
run: ./gradlew test
- name: Run integration tests
run: ./gradlew integrationTest
- name: Generate coverage report
run: ./gradlew jacocoTestReport
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
files: ./build/reports/jacoco/test/jacocoTestReport.xml
- name: Publish test results
uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
Example 5: Coverage Report Verification in CI
# gradle.properties
org.gradle.logging.level=info
org.gradle.parallel=true
# Fail CI if coverage below threshold
jacoco.coverage.minimum=0.60
# Local testing
./gradlew jacocoTestReport
# View report
open build/reports/jacoco/test/html/index.html
# CI will automatically fail if coverage < 0.60
Commands Reference
# === UNIT TESTS ===
./gradlew test # Run all unit tests
./gradlew test --tests "com.example.MyTest" # Run specific class
./gradlew test --tests "*IntegrationTest" # Pattern matching
./gradlew test -x integrationTest # Exclude integration tests
# === INTEGRATION TESTS ===
./gradlew integrationTest # Run integration tests
./gradlew test integrationTest # Both unit and integration
# === CODE COVERAGE ===
./gradlew jacocoTestReport # Generate coverage report
./gradlew jacocoTestCoverageVerification # Verify minimum coverage
./gradlew test jacocoTestReport # Tests + coverage
# === DEBUGGING ===
./gradlew test --info # Detailed logging
./gradlew test --stacktrace # Full stack traces
./gradlew test --debug # Debug mode
# === CONTINUOUS ===
./gradlew test --continuous # Rerun on changes
./gradlew integrationTest --continuous # Watch integration tests
# === CLEANUP ===
./gradlew cleanTest # Clean test outputs
./gradlew clean # Clean all outputs
Troubleshooting
TestContainers connection errors:
# Verify Docker
docker ps
# Check Docker socket
ls -la /var/run/docker.sock
# Set Docker environment (macOS)
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST=unix://${HOME}/.docker/run/docker.sock
Coverage report not generated:
- Ensure
tasks.test { finalizedBy(jacocoTestReport) } - Verify JaCoCo plugin is applied
- Check build directory permissions
Tests fail in CI but pass locally:
- Verify same Java version in CI
- Check environment variables
- Ensure Docker daemon available in CI (for TestContainers)
See Also
- gradle-spring-boot-integration - Spring Boot test starters
- gradle-performance-optimization - Parallel test execution
- gradle-ci-cd-integration - CI/CD test reporting
- JUnit 5 Documentation
- TestContainers Documentation
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です