Gradle Testing Tasks and Test Reports: A Complete Guide
Gradle's test task orchestrates JVM test execution with fine-grained control over filtering, parallelism, JVM configuration, and reporting. Understanding how to configure it properly saves significant time in both development and CI.
Basic Test Configuration
The test task is automatically applied with the java plugin:
// build.gradle
plugins {
id 'java'
}
test {
useJUnitPlatform() // for JUnit 5 / Spock 2.x
// useJUnit() // for JUnit 4
// useTestNG() // for TestNG
}JVM Arguments and System Properties
test {
useJUnitPlatform()
// JVM args
jvmArgs '-Xmx512m', '-XX:+EnableDynamicAgentLoading'
// System properties accessible via System.getProperty()
systemProperty 'spring.profiles.active', 'test'
systemProperty 'app.base.url', 'http://localhost:8080'
// Environment variables
environment 'DATABASE_URL', 'jdbc:h2:mem:test'
// Max heap for test JVM
maxHeapSize = "512m"
}Test Filtering
Run specific tests without modifying code:
# Run all tests in a class
./gradlew <span class="hljs-built_in">test --tests <span class="hljs-string">"com.example.OrderServiceTest"
<span class="hljs-comment"># Run a specific method
./gradlew <span class="hljs-built_in">test --tests <span class="hljs-string">"com.example.OrderServiceTest.should create order"
<span class="hljs-comment"># Wildcard matching
./gradlew <span class="hljs-built_in">test --tests <span class="hljs-string">"*.OrderServiceTest"
./gradlew <span class="hljs-built_in">test --tests <span class="hljs-string">"com.example.*"
<span class="hljs-comment"># Multiple patterns
./gradlew <span class="hljs-built_in">test --tests <span class="hljs-string">"*.OrderServiceTest" --tests <span class="hljs-string">"*.PaymentServiceTest"Filter in the build file for permanent exclusions:
test {
filter {
includeTestsMatching "com.example.unit.*"
excludeTestsMatching "com.example.slow.*"
}
}Parallel Test Execution
test {
useJUnitPlatform()
// Run in parallel using multiple workers
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
// Fork a new JVM every N tests (isolate state)
forkEvery = 100
}maxParallelForks spawns multiple test JVMs. forkEvery limits how many tests run in a single JVM process before forking a fresh one — useful when tests leak static state.
Custom Test Source Sets
Separate unit and integration tests:
// build.gradle
sourceSets {
integrationTest {
groovy {
srcDir 'src/integrationTest/groovy'
}
resources {
srcDir 'src/integrationTest/resources'
}
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
}
}
configurations {
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}
task integrationTest(type: Test) {
description = 'Runs integration tests.'
group = 'verification'
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
useJUnitPlatform()
shouldRunAfter test
}
check.dependsOn integrationTestRun: ./gradlew integrationTest
Test Logging
Control what appears in the console:
test {
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
showExceptions true
showCauses true
showStackTraces true
// Show test output on failure only
showStandardStreams false
afterSuite { desc, result ->
if (!desc.parent) {
println "\nTest Results: ${result.resultType} " +
"(${result.testCount} tests, " +
"${result.successfulTestCount} passed, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped)"
}
}
}
}HTML Test Reports
Gradle generates HTML reports automatically at build/reports/tests/test/index.html. Customize the location:
test {
reports {
html {
outputLocation = file("${buildDir}/reports/unit-tests")
}
junitXml {
outputLocation = file("${buildDir}/test-results/unit")
}
}
}JUnit XML output (build/test-results/) is what CI systems (Jenkins, GitHub Actions) parse for test result visualization.
Test Retry
Add the retry plugin for flaky test handling:
plugins {
id 'org.gradle.test-retry' version '1.5.8'
}
test {
retry {
maxRetries = 2
maxFailures = 5
filter {
includeClasses.add("*.*IntegrationTest")
}
}
}Skipping Tests
# Skip all tests
./gradlew build -x <span class="hljs-built_in">test
<span class="hljs-comment"># Skip specific task
./gradlew integrationTest -x <span class="hljs-built_in">testIn the build file:
test {
onlyIf { !project.hasProperty('skipTests') }
}Run: ./gradlew test -PskipTests
Test Caching
Gradle's build cache skips test execution if inputs haven't changed. Enable in gradle.properties:
org.gradle.caching=trueTests must be deterministic (no timestamps, random seeds) for caching to be reliable. Use @RepeatedTest with a fixed seed in JUnit 5, or @Seed in Spock, when tests involve randomness.
Aggregated Reports with Test Aggregation Plugin
For multi-module projects, aggregate all results into one report:
// build.gradle (root project)
plugins {
id 'test-report-aggregation'
}
dependencies {
testReportAggregation project(':module-a')
testReportAggregation project(':module-b')
}
reporting {
reports {
testAggregateTestReport(AggregateTestReport) {
testType = TestSuiteType.UNIT_TEST
}
}
}Run: ./gradlew testAggregateTestReport
CI Best Practices
test {
useJUnitPlatform()
// Always publish XML for CI parsing
reports {
junitXml.required = true
}
// Don't stop the build on first test failure in CI
ignoreFailures = System.getenv("CI") == "true"
// Parallel on CI (usually more cores available)
maxParallelForks = System.getenv("CI") ? 4 : 1
// Fail fast in local dev
failFast = !System.getenv("CI")
}GitHub Actions example:
- name: Run tests
run: ./gradlew test --continue
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: build/test-results/**/*.xmlUseful Commands
./gradlew test <span class="hljs-comment"># run tests
./gradlew <span class="hljs-built_in">test --rerun-tasks <span class="hljs-comment"># force re-run even if up-to-date
./gradlew <span class="hljs-built_in">test --info <span class="hljs-comment"># verbose output
./gradlew <span class="hljs-built_in">test --debug <span class="hljs-comment"># very verbose
./gradlew <span class="hljs-built_in">test --<span class="hljs-built_in">continue <span class="hljs-comment"># don't stop on first failure
./gradlew dependencies --configuration testRuntimeClasspath <span class="hljs-comment"># debug classpathGradle's test infrastructure is one of its strongest features. With proper configuration, you get fast feedback cycles locally, reliable CI runs, and readable reports — all without external tooling.