skip to content

Suite Dependencies & Production Access

The suite-local dependencies block and the wiring that lets a suite see the main source set and shared libraries. Interviewers ask because suites do not inherit test dependencies the way people assume.

on this pageshow

questions

5

Inside a JVM test suite's dependencies block, how do you give an integrationTest suite access to your project's own main/production classes?

level: juniorimportance: must knowfreq 60%

answer

  1. suite-local dependencies { } block
  2. implementation(project()) = this project
  3. custom suites don't inherit main classpath
  4. deps go to integrationTestImplementation

basics

~10 s

Add implementation(project()) inside the suite's dependencies { } block. The bare project() call refers to the current project, so the suite compiles and runs against main classes.

solid answer

~30 s

Each JVM test suite registered via the `jvm-test-suite` plugin has its own `dependencies { }` block. To let an `integrationTest` suite see the project's own production code you add `implementation(project())` there — `project()` with no path means *this* project. Unlike the built-in `test` suite, a custom suite does **not** automatically depend on the main source set's `implementation`/`runtimeOnly` classpath, so you must wire it explicitly. You can also pull in third-party libraries (`implementation(libs.someLib)`) in the same block; those resolve against the suite's dedicated `integrationTestImplementation` configuration, keeping them isolated from the `test` suite and from `main`.

code

kotlin · 10 lines
kotlin
testing {
  suites {
    val integrationTest by registering(JvmTestSuite::class) {
      dependencies {
        implementation(project())          // see main classes
        implementation(libs.junit.jupiter)
      }
    }
  }
}

go deeper

for a junior

Recall that implementation(project()) inside the suite's dependencies block gives access to main classes.

for a middle

Explain that the suite block maps to a dedicated integrationTestImplementation configuration and that custom suites don't inherit the main classpath.

for a senior

Contrast the special built-in test suite with custom suites, and discuss classpath isolation benefits of suite-specific configurations.

for a principal

Frame suite-local dependency blocks as the mechanism enforcing classpath isolation across test tiers org-wide, avoiding leaked deps between unit and integration tests.

## What the suite `dependencies` block is The `jvm-test-suite` plugin (applied automatically by the `java` plugin) exposes a `testing { suites { } }` container. Each suite you register — e.g. `val integrationTest by registering(JvmTestSuite::class)` — gets its **own** dependency block: ```kotlin testing { suites { val integrationTest by registering(JvmTestSuite::class) { dependencies { implementation(project()) } } } } ``` This block is a small typed DSL (`JvmComponentDependencies`) — it is **not** the top-level `dependencies { }` of the build. Methods like `implementation(...)`, `runtimeOnly(...)`, `compileOnly(...)`, and `annotationProcessor(...)` add to **suite-specific configurations** named after the suite: `integrationTestImplementation`, `integrationTestRuntimeOnly`, etc. ## Why `project()` is needed The built-in `test` suite is special: it inherits the main source set's compile/runtime classpath, so your tests see production classes for free. A **custom** suite is wired from scratch and gets no such inheritance. `implementation(project())` adds a dependency on the current project's outgoing API/runtime variants, so the suite can `import` and execute your main classes. `project()` (empty args) = the project the build script belongs to; `project(":other")` would target a different subproject. ## Pulling in shared libraries Inside the same block you reference version-catalog or coordinate dependencies just like a normal configuration: ```kotlin dependencies { implementation(project()) implementation(libs.junit.jupiter) runtimeOnly(libs.postgresql) } ``` Because these land in `integrationTestImplementation`, they are isolated from `testImplementation` — the unit-test suite never sees them, which keeps the two classpaths clean. ## Key takeaway Custom suites are isolated by default; `implementation(project())` is the explicit bridge from a suite to your own production code, and the suite block is where all of that suite's library deps live.

  • Why doesn't the custom integrationTest suite already see main classes the way the test suite does?
    The built-in `test` suite is special-cased to inherit the main source set's compile/runtime classpath. Custom suites are wired independently and inherit nothing, so you add `implementation(project())` explicitly.
  • What configuration does `implementation(...)` inside the integrationTest suite actually contribute to?
    `integrationTestImplementation` — a suite-specific configuration created by the plugin, isolated from `testImplementation` and from `main`'s `implementation`.

saying these in an interview costs you the question

  • Claiming `project()` needs the project path of the current project — bare `project()` already means the current project.
  • Assuming a custom suite inherits the main classpath automatically like the `test` suite does.

context

open as a page

A teammate's custom integrationTest suite fails to compile with 'cannot find symbol' for classes from a library that the unit tests use fine. What's the likely cause and fix, given how suite dependencies work?

level: middleimportance: must knowfreq 40%

basics

~20 s

Custom suites don't inherit testImplementation. The library was declared only for the test suite, so the integration suite never saw it. Fix: declare it in the integrationTest suite's dependencies { }, or make integrationTestImplementation extend testImplementation.

open as a page

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?

level: middleimportance: should knowfreq 30%

basics

~10 s

Each 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.

open as a page

When wiring an integrationTest suite's dependencies, when would you depend on testFixtures(project()) instead of just project()?

level: middleimportance: should knowfreq 35%

basics

~10 s

project() gives the suite your production classes. testFixtures(project()) additionally exposes shared test-only helpers (builders, fakes, fixtures) published by the java-test-fixtures plugin, so multiple suites reuse them.

open as a page

In a multi-module build, how do you make an integrationTest suite in one module depend on another module's production classes, and what are the trade-offs of doing so?

level: seniorimportance: should knowfreq 25%

basics

~10 s

Use implementation(project(":otherModule")) inside the suite's dependencies block. It adds a project dependency on that module's outgoing API/runtime variants. The trade-off is tighter build-graph coupling and recompilation when the dependency changes.

open as a page