A developer runs `./gradlew test --tests 'com.example.FooTest'` twice and the second run reports UP-TO-DATE without executing the test. Why, and how do they force it to run?
answer
- Test task is incremental
- filter is an input
- same command -> UP-TO-DATE
- --rerun-tasks / --rerun
- cleanTest invalidates outputs
basics
~10 sThe test task is up-to-date because no inputs changed, so Gradle skips it. Force a re-run with --rerun-tasks (or cleanTest, or in newer Gradle the task's --rerun option).
solid answer
~40 sGradle's `Test` task is incremental: if its inputs (test classes, runtime classpath, system properties, the filter) and outputs are unchanged since the last successful run, the task is marked UP-TO-DATE and skipped entirely — it does not re-execute the test. The `--tests` filter is part of the task's inputs, so changing the pattern triggers a run, but repeating the *same* command with no code change will not. To force execution use `--rerun-tasks` (re-runs everything in the graph ignoring up-to-date checks), the task-scoped `--rerun` option (`./gradlew test --rerun --tests '...'`), or invalidate outputs with `cleanTest test`. People often confuse this skip with a passing run; it just means nothing changed. This is the build-cache/incremental behavior interacting with selective execution.
code
bash · 5 lines# force just the test task to run again
./gradlew test --rerun --tests 'com.example.FooTest'
# or ignore up-to-date for the whole graph
./gradlew test --tests 'com.example.FooTest' --rerun-tasksgo deeper
Recognize UP-TO-DATE means the task was skipped, and --rerun-tasks forces it.
Explain that the filter and classes are task inputs, so an identical command is skipped; name --rerun/cleanTest.
Distinguish --rerun vs --rerun-tasks, mention build-cache hits, and why not to disable up-to-date in the build.
Set policy on when forced reruns are acceptable in CI vs. relying on incrementality and caching for speed.
## Why the skip happens Gradle tracks each task's **inputs** and **outputs**. For the `Test` task, inputs include the compiled test classes, the runtime classpath, system properties, JVM args, and the **test filter** (the `--tests` patterns). Outputs include the binary test results and reports. If a task ran successfully and none of its inputs/outputs have changed, Gradle declares it **UP-TO-DATE** and skips execution. So the second identical `--tests` run does nothing — which is correct incremental behavior, not a bug. The build cache can compound this: a cache **hit** restores prior results without running the JVM at all. ## Filter is an input Because the `--tests` value participates in the input fingerprint, **changing** the pattern (e.g. adding a method, switching classes) makes the task out-of-date and it runs. But re-issuing the *same* selection with no source change leaves the fingerprint identical. ## How to force a run ```bash # re-run the whole invoked graph, ignoring up-to-date results ./gradlew test --tests 'com.example.FooTest' --rerun-tasks # task-scoped rerun option (Gradle 7.5+) ./gradlew test --rerun --tests 'com.example.FooTest' # invalidate this task's outputs, then run ./gradlew cleanTest test --tests 'com.example.FooTest' ``` - `--rerun-tasks` disables up-to-date checks for every task in the requested graph. - `--rerun` is the per-task option (preferred when you only want to re-run `test`). - `cleanTest` deletes the test outputs so the task has nothing to be up-to-date against. ## When you actually need this Flaky tests, time/environment-dependent behavior, or simply wanting to watch a test execute again. For deterministic suites, the skip is a feature — it saves time. ## Don't disable caching wholesale Avoid `outputs.upToDateWhen { false }` baked into the build just to always re-run; that defeats incrementality for everyone. Use the CLI flags for one-off local needs.
- Does UP-TO-DATE mean the test passed?No. It means Gradle skipped the task because inputs/outputs were unchanged. The prior result (which was a pass, or it wouldn't be up-to-date) is reused, but the JVM did not run this time.
- What's the difference between `--rerun` and `--rerun-tasks`?`--rerun` is a task option that forces just that task to re-execute; `--rerun-tasks` is a global flag that disables up-to-date checks for every task in the requested build, so it re-runs dependencies too.
- Why not bake `outputs.upToDateWhen { false }` into the test task?It permanently disables incrementality, forcing the suite to re-run on every build for everyone and wasting CI time. Use the one-off CLI flags instead.
saying these in an interview costs you the question
- Interpreting UP-TO-DATE as a test failure or as the test having actually run.
- Recommending permanent `upToDateWhen { false }` instead of a one-off rerun flag.