How do you reference or extend a JVM test suite's underlying SourceSet — for example to add an extra source directory or wire a custom configuration that isn't expressible in the suite's dependencies block?
answer
- suite.sources -> underlying SourceSet
- implementationConfigurationName -> integrationTestImplementation
- extendsFrom for inheriting test classpath
- java.srcDir for extra source roots
- DSL can't express extendsFrom
basics
~10 sEach suite exposes its SourceSet via sources (integrationTest.sources). Use it to add directories (java.srcDir(...)) or to grab generated configuration names like integrationTestImplementation for extendsFrom wiring not covered by the dependencies DSL.
solid answer
~30 sA `JvmTestSuite` wraps a real `SourceSet`, reachable through its `sources` property. The suite's `dependencies { }` block is the high-level path, but when you need lower-level control you drop to `sources.sourceSet`. Common uses: add extra source roots (`integrationTest.sources.java.srcDir("src/it/java")`), reference the source set's resources, or pull the **configuration names** it owns — e.g. `sourceSets["integrationTest"].implementationConfigurationName` resolves to `integrationTestImplementation`, which you can then `extendsFrom(configurations.testImplementation.get())` so the integration suite inherits everything the unit-test suite declares. This `extendsFrom` trick is the canonical way to make an integration suite reuse the full `test` classpath instead of re-declaring shared libraries.
code
kotlin · 14 lines// inherit the full unit-test classpath into the integration suite
configurations["integrationTestImplementation"]
.extendsFrom(configurations["testImplementation"])
configurations["integrationTestRuntimeOnly"]
.extendsFrom(configurations["testRuntimeOnly"])
// add an extra source directory via the suite's SourceSet
testing {
suites {
val integrationTest by getting(JvmTestSuite::class) {
sources { java.srcDir("src/it/java") }
}
}
}go deeper
Awareness that a suite has an underlying source set reachable via sources is sufficient.
Show how to add source dirs through sources and fetch generated configuration names for wiring.
Use extendsFrom to inherit the unit-test classpath and explain when manual compileClasspath += main.output is appropriate vs implementation(project()).
Define conventions for classpath inheritance across test tiers to minimize duplication while keeping suite isolation predictable across many modules.
## Suite ↔ SourceSet relationship Registering a suite creates a matching `SourceSet`: ```kotlin val integrationTest by registering(JvmTestSuite::class) // creates source set 'integrationTest' under src/integrationTest/{java,kotlin,resources} ``` The suite object exposes it via `sources`: ```kotlin integrationTest { sources { java.srcDir("src/it/java") // extra source dir resources.srcDir("src/it/resources") } } ``` ## Configuration names Every source set owns a family of configurations whose names you can fetch programmatically: - `implementationConfigurationName` -> `integrationTestImplementation` - `runtimeOnlyConfigurationName` -> `integrationTestRuntimeOnly` - `compileOnlyConfigurationName`, `annotationProcessorConfigurationName`, etc. This matters because the suite `dependencies { }` DSL only lets you *add* dependencies; it can't express `extendsFrom`. For inheritance you go to the configuration directly: ```kotlin configurations["integrationTestImplementation"] .extendsFrom(configurations["testImplementation"]) configurations["integrationTestRuntimeOnly"] .extendsFrom(configurations["testRuntimeOnly"]) ``` Now the integration suite inherits every library the unit-test suite declared — no duplication. ## Wiring against main output explicitly If you don't want to use `implementation(project())`, you can wire the source set's compile/runtime classpath directly against the main source set output: ```kotlin val integrationTest = sourceSets["integrationTest"] integrationTest.compileClasspath += sourceSets["main"].output integrationTest.runtimeClasspath += sourceSets["main"].output ``` This is the older, more manual idiom; `implementation(project())` in the suite block is the modern equivalent and is preferred. ## When to drop down to `sources` Use the high-level `dependencies { }` block for normal library/project deps. Reach into `sources` / the configuration names only for things the DSL can't express: extra source directories, `extendsFrom` inheritance, or manual classpath augmentation. ## Takeaway `sources` is the escape hatch from the suite DSL to the underlying `SourceSet` and its generated configurations — the bridge for inheritance and custom wiring beyond plain dependency declarations.
- Why can't you express `extendsFrom` inside the suite's `dependencies { }` block?That block only adds dependencies to suite configurations; configuration inheritance is a property of the `Configuration` object, so you must reach the configuration (via `configurations[...]` or the source set's `*ConfigurationName`) and call `extendsFrom`.
- What does `sourceSets["integrationTest"].implementationConfigurationName` return?The string `integrationTestImplementation` — the name of the suite's implementation configuration, useful for programmatic wiring.
saying these in an interview costs you the question
- Claiming the suite `dependencies { }` block supports `extendsFrom` — it does not.
- Forgetting that adding a source dir requires going through the suite's `sources`/SourceSet, not the dependencies block.