Gradle Tests Not Rerun: UP-TO-DATE, Cache, and Missing Inputs
Distinguish legitimate Gradle test reuse from an undeclared-input bug. Check task outcomes, force execution, and declare the state the test reads.
A Gradle test task marked UP-TO-DATE or FROM-CACHE reuses a previous outcome. That can be correct and useful. The risk appears when a test reads state absent from its declared inputs, so Gradle has no reason to invalidate the outcome when that state changes.
This guide explains that mechanism with a hypothetical external flag file. It removes the earlier article’s unretained timing measurements and model success ratios. The commands are diagnostic procedures to run on your own build, not a new benchmark.
Read the task outcome
Use a log mode that preserves task labels:
./gradlew test --console=plain
Distinguish a task that executed from UP-TO-DATE, FROM-CACHE, NO-SOURCE, SKIPPED, or an explicitly excluded task. The Gradle testing guide describes test execution and filtering. Overall success means the selected work did not fail; it does not promise every test was freshly executed.
A fast run is not proof of a problem. It may be legitimate reuse, a small suite, or a different selected task graph. Use the outcome and reports, not a millisecond threshold.
Construct the missing-input scenario
Suppose a test reads flags/discount-enabled.txt directly from the project root. It passes when the file contains true. If the build does not declare that file as a task input, changing it may not invalidate the test outcome.
Reproduce the sequence in a disposable project: run with true, change only the flag to false, inspect the task outcome, then force execution. Keep the source, configuration, and logs if reporting the result. A cache hit is evidence about declared inputs; it is not proof the external flag still has the expected value.
Force the relevant verification
Check wrapper support for task-level --rerun. A broadly available diagnostic is:
./gradlew test --rerun-tasks --no-build-cache --console=plain
This intentionally forgoes reuse for the selected invocation. It can cost more work, so it is a diagnostic choice rather than a reason to disable caching for every build permanently.
Compare actual failures and reports after the forced run. A result-file modification time can reflect copying from a cache. Internal timestamps can provide a clue, but they are not a universal proof of freshness across reporters and configurations.
Declare what the test reads
For the hypothetical file, the Groovy DSL can include:
tasks.named('test') {
useJUnitPlatform()
inputs.file('flags/discount-enabled.txt')
.withPathSensitivity(PathSensitivity.NONE)
}
Choose path sensitivity from the test’s semantics. NONE is appropriate when only content matters; it is wrong if behavior depends on the path. The Gradle input/output reference explains how declared state participates in reuse.
Verify both directions: the bad flag must invalidate a good result, the corrected flag must permit a successful result, and unchanged valid inputs may legitimately reuse it. Account for environment variables, external services, and time-dependent behavior separately; one file declaration cannot describe them all.
Compare runners accurately
Maven Surefire’s default naming patterns can omit a class; Gradle reuse can avoid executing a selected task. Those are different diagnoses. Both runners also have configuration-dependent filtering, discovery, and engine behavior, so “Maven hides files, Gradle discovers everything” is too broad.
An explicitly requested nonexistent Maven test need not silently pass; check Surefire’s selection-failure settings. Use the Maven discovery guide for that investigation.
A verification receipt is a third, separate layer: it reports the cases the runner executed. The trustbut Maven receipts return PASS both for a test Surefire never discovered and for a new test whose only assertion is assertTrue(true).
Require an honest completion record
When an agent runs the build, ask which task executed and which outcome was reused. If the acceptance requirement calls for fresh external-service verification, request that explicitly and record environmental blockers.
Review the JUnit assertions once execution is established. The CI contract guide covers acceptance beyond exit status, and the Boot 4 instructions show how to encode the expectation. The Java hub links the related scenarios.
Peng Zhou
AI Kit Guides is dedicated to helping you navigate the world of AI tools with honest, practical, and beginner-friendly content.
Tags: