Walk through how Gradle, after useJUnitPlatform(), discovers and runs your Jupiter tests at execution time.
answer
- fork test worker JVMs
- LauncherFactory.create()
- ServiceLoader finds engines
- engine.discover → TestDescriptor tree
- listener streams events → reports
basics
~20 sGradle starts a test worker, creates the Platform Launcher, which uses ServiceLoader to find the Jupiter engine, asks it to discover @Test classes on the test classpath, then executes them and streams results back to Gradle for reporting.
solid answer
~40 sAfter `useJUnitPlatform()`, Gradle forks one or more **test worker** JVMs with the test runtime classpath. In each worker, Gradle's JUnit Platform integration creates a `Launcher` via `LauncherFactory`. The launcher uses Java's **ServiceLoader** to find every `TestEngine` on the classpath — `junit-jupiter-engine` registers itself there. Gradle builds a `LauncherDiscoveryRequest` (which classpath roots and filters to scan); each engine returns a `TestDescriptor` tree of the tests it owns. The Jupiter engine scans for classes containing `org.junit.jupiter.api.Test` methods, lifecycle hooks, etc. The launcher then executes the descriptor tree, and a `TestExecutionListener` registered by Gradle forwards start/finish/skip/fail events back to the Gradle process, which aggregates them into the HTML/XML reports. So the dependency you add (the engine) is precisely what makes discovery work.
code
bash · 2 lines./gradlew test --info # shows worker startup and per-test events
# Reports land at build/reports/tests/test/index.htmlgo deeper
Know that the engine discovers tests and Gradle runs them and reports.
Sequence worker fork → launcher → ServiceLoader → engine.discover → events → reports.
Tie each step to the artifact responsible (launcher, engine) and explain failure points.
Use the model to reason about parallel forks, classpath isolation, and custom engines across a large suite.
## The pipeline end to end ### 1. Task selects the framework `useJUnitPlatform()` records that the `Test` task should drive tests through the JUnit Platform rather than the JUnit 4 runner. ### 2. Test workers fork Gradle starts one or more **test worker** processes (controlled by `maxParallelForks`) using the **test runtime classpath** — which must include `junit-jupiter-engine` and `junit-platform-launcher`. ### 3. Launcher creation Inside the worker, Gradle's Platform integration calls `LauncherFactory.create()` to get a `Launcher`. ### 4. Engine discovery via ServiceLoader The launcher enumerates `TestEngine` implementations through the JDK **ServiceLoader**. Each engine jar contains `META-INF/services/org.junit.platform.engine.TestEngine` naming its class. The Jupiter engine and any vintage/third-party engine register here. ### 5. Test discovery Gradle constructs a `LauncherDiscoveryRequest` selecting classpath roots plus any include/exclude tag or package filters. The launcher calls `engine.discover(request)`; each engine returns a `TestDescriptor` tree. Jupiter's engine finds classes with `@Test`, `@TestFactory`, `@ParameterizedTest`, nested classes, etc. ### 6. Execution and events The launcher executes the merged plan. Gradle registers a `TestExecutionListener`; the Platform fires `executionStarted` / `executionFinished` / `executionSkipped` callbacks. Gradle translates these into its own test events. ### 7. Reporting Gradle aggregates events into the binary results, then the HTML report under `build/reports/tests/test` and the JUnit XML under `build/test-results/test`. ```bash # See discovered/run tests with detail: ./gradlew test --info # HTML report: open build/reports/tests/test/index.html ``` ## Why this matters It explains why merely adding the API is insufficient (no engine to discover), and why a missing launcher breaks the whole chain at step 3.
- What component does the actual @Test discovery — Gradle or JUnit?The Jupiter TestEngine does the discovery. Gradle only orchestrates: it forks the worker, creates the launcher, supplies the discovery request, and consumes events.
- Where do the test reports end up?HTML at build/reports/tests/test/index.html and JUnit-format XML at build/test-results/test/, both produced by Gradle from the streamed Platform events.
saying these in an interview costs you the question
- Saying Gradle itself scans bytecode for @Test — discovery is delegated to the engine.
- Forgetting that discovery happens in a forked worker on the test runtime classpath.