skip to content

In a multi-module build with a custom `integrationTest` task, how do you use `--tests` correctly, and what pitfalls arise?

level: middleimportance: should knowfreq 40%

answer

  1. option lives on each Test task
  2. name integrationTest explicitly
  3. root run fans filter to all modules
  4. :module:test to scope
  5. no-match -> task fails per module

basics

~10 s

Attach --tests to the specific Test task you mean: ./gradlew integrationTest --tests 'com.example.FooIT'. In multi-module builds, also scope by project path (e.g. :app:test) so only the right module's task gets the filter.

solid answer

~40 s

`--tests` is an option on a particular `Test` task instance. The default `test` task only knows about the unit-test source set; a custom `integrationTest` task (its own source set) won't run with `./gradlew test --tests ...`. You must name the task: `./gradlew integrationTest --tests 'com.example.OrderIT'`. In a multi-module build, `./gradlew test --tests 'X'` runs the `test` task of every project that has it, applying the same filter everywhere — projects where nothing matches will fail unless empty-match is allowed. Scope to a single module with the task path: `./gradlew :payments:test --tests 'com.example.PaymentTest'`. So the two pitfalls are: applying `--tests` to the wrong task (unit vs. integration), and accidentally fanning the filter out across all modules and tripping the no-match failure.

code

bash · 5 lines
bash
# target a custom Test task
./gradlew integrationTest --tests 'com.example.OrderIT'

# scope the filter to one module's unit tests
./gradlew :payments:test --tests 'com.example.PaymentTest'

go deeper

for a junior

Know to put --tests after the task you want to run.

for a middle

Explain per-task scoping, the unit-vs-integration task distinction, and using :module:test to avoid fan-out.

for a senior

Discuss the no-match failure semantics across modules and when to set isFailOnNoMatchingTests=false.

for a principal

Advise on suite/task topology (JVM Test Suite plugin) so selection stays predictable at scale across many modules.

## `--tests` is per-task, not global The option is declared on the `Test` task type. Each `Test` task has its own classpath and test source set. The Java plugin creates `test` for `src/test`. If you add an integration suite — commonly via `tasks.register<Test>("integrationTest")` bound to an `integrationTest` source set, or via the JVM Test Suite plugin — that task is **separate** and is not reached by `./gradlew test --tests ...`. ```kotlin val integrationTest by tasks.registering(Test::class) { testClassesDirs = sourceSets["integrationTest"].output.classesDirs classpath = sourceSets["integrationTest"].runtimeClasspath useJUnitPlatform() } ``` To run one integration test: `./gradlew integrationTest --tests 'com.example.OrderIT'`. ## Fan-out across modules When you run `./gradlew test --tests 'com.example.OrderTest'` from the root of a multi-module build, Gradle invokes the `test` task in **every** subproject that declares it, and passes the same `--tests` filter to each. Modules where the pattern matches nothing will fail the task with **"No tests found for given includes"**. The class you want lives in exactly one module, so the other modules' `test` tasks are noise (and can break the build). ## Scope with a task path Prefix the task with the project path to target one module: ```bash ./gradlew :payments:test --tests 'com.example.PaymentTest' ``` Now only `:payments`'s `test` task runs and receives the filter. ## Allowing empty matches If you genuinely want to broadcast a filter and tolerate non-matching modules, set in the affected tasks: ```kotlin tasks.withType<Test>().configureEach { filter { isFailOnNoMatchingTests = false } } ``` ## Summary of pitfalls 1. **Wrong task** — `test` vs. `integrationTest`/`functionalTest`. 2. **Unscoped fan-out** — root invocation pushes the filter to all modules, tripping no-match failures. 3. **Forgetting the engine** — the task must be configured with `useJUnitPlatform()`/`useTestNG()` for the filter to resolve as expected.

  • Why does `./gradlew test --tests 'X'` sometimes fail in a module that has no matching test?
    By default a Test task fails with "No tests found for given includes" when the filter matches nothing. At the root the filter is applied to every module's `test` task, so non-matching modules fail. Scope with `:module:test` or set `isFailOnNoMatchingTests = false`.
  • How would you select an integration test instead of a unit test?
    Name the integration task: `./gradlew integrationTest --tests '...'` (or `:module:integrationTest --tests '...'`), because `--tests` only filters the task it is attached to.

saying these in an interview costs you the question

  • Assuming `--tests` is a global selector that finds tests in any task or module.
  • Expecting `./gradlew test --tests ...` to run integration/functional suites that live in a separate source set/task.

context