skip to content

Selection & Reporting

Choosing which tests run and what the run produces: the --tests filter, tag-based filtering, HTML and XML reports, and cross-module aggregation. Interviewers ask because CI consumes exactly these outputs.

on this pageshow

explore

questions

20

What is the `test-report-aggregation` plugin in Gradle and what problem does it solve in a multi-project build?

level: juniorimportance: must knowfreq 55%

answer

  1. core plugin, no version
  2. one HTML report from many subprojects
  3. testAggregateTestReport task
  4. testReportAggregation configuration
  5. variant-aware, not file paths

basics

~10 s

It's a core Gradle plugin that merges the test results of multiple subprojects into a single combined HTML report, so you don't have to open each subproject's report separately.

solid answer

~40 s

`test-report-aggregation` is a built-in Gradle plugin (no external dependency) that collects the test results produced across many subprojects and renders one consolidated HTML report. In a multi-project build, each subproject normally produces its own `build/reports/tests/test/index.html`; reviewing them one by one is tedious and gives no project-wide pass/fail view. You apply the plugin (usually on an aggregating project such as a dedicated `test-report` project or the application project), it adds a `testAggregateTestReport` task, and running it gathers the binary test result data from every project it depends on and writes a single HTML report under `build/reports/tests/...`. It uses variant-aware dependency resolution to find result data, so it only needs project dependencies declared, not manual wiring of file paths.

code

kotlin · 10 lines
kotlin
plugins {
    id("test-report-aggregation")
}

dependencies {
    testReportAggregation(project(":service-a"))
    testReportAggregation(project(":service-b"))
}

// Run: ./gradlew testAggregateTestReport

go deeper

for a junior

Know it produces one combined test report across subprojects and adds a testAggregateTestReport task.

for a middle

Explain it merges binary result data via project dependencies on a testReportAggregation configuration, not file copying.

for a senior

Discuss applying it on a dedicated aggregation project and how variant-aware resolution supplies the result data.

for a principal

Position it within a standardized multi-project reporting strategy alongside jacoco-report-aggregation and a CI artifact convention across teams.

## The problem In a Gradle multi-project build, every project that runs tests produces its own report. By default `Test` tasks write: - Binary result data to `build/test-results/test/` (used by tooling) - A human HTML report to `build/reports/tests/test/index.html` With ten subprojects you get ten separate HTML reports and no single "did everything pass?" view. CI dashboards and humans both want one artifact. ## What the plugin does The **`test-report-aggregation`** plugin is a *core* Gradle plugin (ships with the distribution — no `dependencies {}` entry, no version). Applying it: 1. Adds a resolvable configuration that knows how to ask other projects for their *test result data* (not their HTML — the raw binary results). 2. Registers a task named **`testAggregateTestReport`** of type `AggregateTestReport`. 3. Wires that task to merge the collected binary results into ONE HTML report, typically at `build/reports/tests/unit-test/aggregated-results/index.html`. ## How it finds the data It relies on **variant-aware resolution**. You declare ordinary project dependencies, and the plugin requests the `test-results` outgoing variant from each. This is why you don't hand-wire file paths — Gradle's dependency graph carries the result data via attributes. ```kotlin plugins { id("test-report-aggregation") } dependencies { testReportAggregation(project(":service-a")) testReportAggregation(project(":service-b")) } reporting { reports { val testAggregateTestReport by getting(AggregateTestReport::class) { testSuiteName = "test" } } } ``` Run `./gradlew testAggregateTestReport` and you get one combined report. ## Where to apply it A common pattern is a dedicated project (e.g. `:test-report`) that depends on all the others purely to aggregate. The `jvm-test-suite` and `java` plugins also integrate so the `application` or `java` plugin's project can host the aggregation. The sibling **`jacoco-report-aggregation`** plugin does the same idea for coverage.

  • Is `test-report-aggregation` a third-party plugin you add a version for?
    No. It is a core Gradle plugin bundled with the distribution, so you apply it by id with no version and no buildscript dependency.
  • What task name does it add?
    `testAggregateTestReport` (of type `AggregateTestReport`), which produces the merged HTML report.

Like a teacher collecting every student's individual quiz sheet and stapling them into one gradebook instead of leaving ten loose papers on the desk.

saying these in an interview costs you the question

  • Saying you must manually copy each subproject's HTML into one folder — the plugin merges binary result data, not HTML.
  • Treating it as a community plugin that needs a version number.

context

open as a page

What does the `filter {}` block inside a `Test` task do, and how do `includeTestsMatching` and `excludeTestsMatching` work?

level: juniorimportance: must knowfreq 55%

basics

~10 s

The filter {} block on a Test task selects which tests run by pattern. includeTestsMatching('*Foo*') keeps only matching tests; excludeTestsMatching drops matching ones. Patterns match fully-qualified class/method names with * wildcards.

open as a page

After running `./gradlew test`, where does Gradle put the human-readable test report and the machine-readable results, and which one do you open in a browser?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Gradle writes an HTML report under build/reports/tests/test/ (open index.html in a browser) and machine-readable JUnit-XML under build/test-results/test/. You open the HTML in a browser.

open as a page

How do you run a single test class or a single test method from the command line with Gradle?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Use the --tests flag on the test task: ./gradlew test --tests 'com.example.MyTest' runs one class, and ./gradlew test --tests 'com.example.MyTest.myMethod' runs one method.

open as a page

How do you set up test report aggregation across several subprojects? Walk through applying the plugin and declaring what gets aggregated.

level: middleimportance: must knowfreq 50%

basics

~10 s

Apply test-report-aggregation on an aggregating project, add each subproject via the testReportAggregation dependency configuration, then run testAggregateTestReport to get one merged HTML report.

open as a page

How do you partition tests by `@Tag` using `useJUnitPlatform { includeTags / excludeTags }`?

level: middleimportance: must knowfreq 50%

basics

~10 s

Annotate tests with JUnit 5 @Tag("slow"). In the Test task call useJUnitPlatform { includeTags("slow") } to run only tagged tests, or excludeTags("slow") to skip them. The platform discovers tests by tag.

open as a page

How do you configure the test reports on a `Test` task — for example disable the HTML report on CI, keep the JUnit-XML, and move the XML to a custom directory?

level: middleimportance: must knowfreq 55%

basics

~10 s

Use the reports {} block on the test task: set html.required = false, junitXml.required = true, and point junitXml.outputLocation at the directory you want.

open as a page

How do wildcard patterns work with Gradle's `--tests` flag, and what can you match with them?

level: middleimportance: must knowfreq 55%

basics

~10 s

The * wildcard matches any sequence of characters. So --tests 'com.example.*Test' runs all classes in com.example ending in Test, and --tests '*OrderTest.should*' runs methods starting with should.

open as a page

Your CI shows green even when tests fail to compile or the build crashes before tests run. How should CI consume Gradle's JUnit-XML so test results are surfaced correctly?

level: seniorimportance: must knowfreq 48%

basics

~10 s

Point the CI test-reporter at **/build/test-results/test/*.xml, upload it as an artifact, and make sure the publish step runs even when the Gradle step fails (e.g. if: always()) so failures and crashes are still reflected.

open as a page

When should you use name-based `filter {}` versus tag-based `useJUnitPlatform { includeTags }`, and how do they interact on the same task?

level: middleimportance: should knowfreq 38%

basics

~20 s

Use name filters for structural/package selection (*IntegrationTest); use tags for semantic categories that survive renames (slow, db). On one task both apply: a test must pass the name filter AND the tag filter to run.

open as a page

A developer complains that test `println`/log output 'disappears' and isn't shown on the console. Where does Gradle put that output by default, and how do the reports relate to it?

level: middleimportance: should knowfreq 34%

basics

~20 s

By default Gradle captures each test's stdout/stderr and folds it into the test reports (HTML page per class and, optionally, the JUnit-XML) instead of streaming it to the console. Open the HTML report to see it, or enable testLogging.showStandardStreams.

open as a page

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%

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.

open as a page

Why does test report aggregation merge binary result data rather than the generated HTML reports, and what attribute/variant mechanism makes that work?

level: seniorimportance: should knowfreq 35%

basics

~20 s

HTML is already-rendered output that can't be cleanly merged. Gradle instead collects each project's raw binary test results via a dedicated outgoing variant selected by attributes, then renders one fresh report from all of them.

open as a page

When you run `testAggregateTestReport` in CI and some subproject tests fail, what happens to the report and the build, and how do you make the report reliably available?

level: seniorimportance: should knowfreq 30%

basics

~10 s

A failing test fails its Test task, which can stop the build before aggregation runs. Use --continue (or finalizers) so all tests run and the aggregate report is still produced even when something fails.

open as a page

Design a multi-task test partitioning scheme so unit tests run fast by default and slow/integration tests run on demand, using filter blocks and tags.

level: seniorimportance: should knowfreq 32%

basics

~10 s

Tag slow tests @Tag("slow"). Make the default test task excludeTags("slow"). Register a separate integrationTest Test task that includeTags("slow"), give it its own classpath, and wire check to depend on it in CI.

open as a page

What are the common pitfalls of test selection — pattern semantics, the JUnit 4 vs Platform divide, and silently dropped tests?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Pitfalls: assuming patterns are regex (they're glob * only); using @Tag/includeTags while still on useJUnit() (JUnit 4 ignores them); and name-based excludes silently missing tests after renames. Also failOnNoMatchingTests defaulting on can break filtered runs.

open as a page

Explain how Gradle's HTML test report relates to the binary test results, and why you can regenerate the HTML without re-running the tests.

level: seniorimportance: should knowfreq 30%

basics

~20 s

Gradle records raw results into a binary result store during the test run; both the HTML report and the JUnit-XML are rendered from that binary data, so the HTML is a derived view, not the primary record.

open as a page

When is `--tests` the right tool versus committed filter configuration or test-suite splitting, and how does that shape a team's local-vs-CI test strategy?

level: seniorimportance: should knowfreq 25%

basics

~20 s

--tests is a transient, local-iteration tool you type by hand to run a focused subset. Durable selection (which suites run, tags, splits) belongs in the build script or CI config, not in an ad-hoc CLI flag.

open as a page

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?

level: seniorimportance: should knowfreq 35%

basics

~10 s

The 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).

open as a page

How would you architect cross-project reporting so that unit and integration test suites each get their own aggregated report, and how does this relate to coverage aggregation?

level: principalimportance: nice to knowfreq 18%

basics

~10 s

Model each suite with jvm-test-suite, then register one AggregateTestReport per suite name (e.g. test and integrationTest). For coverage, apply the sibling jacoco-report-aggregation plugin, which works the same way for JaCoCo data.

open as a page