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?
answer
- reports { html / junitXml }
- required = Property<Boolean>
- outputLocation = DirectoryProperty
- layout.buildDirectory not $buildDir
- mergeReruns / outputPerTestCase
- withType<Test>().configureEach
basics
~10 sUse the reports {} block on the test task: set html.required = false, junitXml.required = true, and point junitXml.outputLocation at the directory you want.
solid answer
~40 sEach `Test` task exposes a `reports` container with `html` and `junitXml` sub-reports. In modern Gradle (8.x) you toggle each with the lazy `required` property and relocate it with `outputLocation`: ```kotlin tasks.test { reports { html.required = false junitXml.required = true junitXml.outputLocation = layout.buildDirectory.dir("my-results") } } ``` Disabling HTML saves time on CI where nobody opens it, while keeping JUnit-XML for the CI parser. `required` is a `Property<Boolean>` and `outputLocation` is a `DirectoryProperty`, so both are lazy/Provider-based — that's why you assign with `=` (Gradle 8 supports the property-assignment syntax). On older Groovy builds you'd see `html.enabled` and `html.destination`, which are deprecated aliases for `required`/`outputLocation`.
code
kotlin · 9 linestasks.test {
reports {
html.required = false
junitXml.required = true
junitXml.outputLocation = layout.buildDirectory.dir("my-results")
junitXml.outputPerTestCase = true
junitXml.mergeReruns = true
}
}go deeper
Know there's a reports {} block and you can turn html/junitXml on or off.
Configure required + outputLocation, know they're lazy properties, name the deprecated aliases.
Apply policy across all test tasks via withType<Test>().configureEach, use mergeReruns/outputPerTestCase appropriately.
Standardize report config in a convention plugin so every module emits CI-consumable XML uniformly.
## The reports container A `Test` task implements `Reporting<TestTaskReports>`, exposing `reports {}` with exactly two sub-reports: - `reports.html` — a `DirectoryReport` rendering the browsable site. - `reports.junitXml` — a `JUnitXmlReport` writing `TEST-*.xml`. Each has two key knobs: - **`required`** — a `Property<Boolean>` (lazy). `true` generates that report, `false` skips it. This replaced the older `enabled` flag. - **`outputLocation`** — `html` uses a `DirectoryProperty`; `junitXml` also uses a `DirectoryProperty`. Setting it relocates where that report is written, overriding the `build/reports/tests/<task>` / `build/test-results/<task>` defaults. ## Lazy properties — why `=` works These are Gradle **lazy properties** (`Property`/`DirectoryProperty`), part of the Provider API. You should source paths from `layout.buildDirectory` rather than hardcoding `"$buildDir/..."` (the latter is deprecated). Kotlin DSL in Gradle 8 supports direct property assignment (`html.required = false`); the explicit form is `html.required.set(false)`. ## JUnitXmlReport extras `junitXml` carries a couple of useful options: - `outputPerTestCase` — when `true`, captures stdout/stderr per test case into the XML (heavier but richer for CI drill-down). - `mergeReruns` — groups retried executions of the same test into a single `<testcase>` with `<rerunFailure>`/`<flakyFailure>` entries, which CI tools use to surface flaky tests instead of double-counting. ## Typical CI shape ```kotlin tasks.withType<Test>().configureEach { reports { html.required = false // nobody browses HTML on CI junitXml.required = true // CI parses this junitXml.mergeReruns = true // collapse retries into flaky markers } } ``` Using `withType<Test>().configureEach { }` applies the policy to *every* test task (including custom `integrationTest`) lazily, so newly registered tasks pick it up too. ## Legacy aliases Groovy builds and old docs use `html.enabled` / `html.destination`. These are deprecated aliases for `required` / `outputLocation`; prefer the new names.
- What's the modern replacement for the deprecated `html.enabled` and `html.destination`?`html.required` (a `Property<Boolean>`) and `html.outputLocation` (a `DirectoryProperty`). The old names are deprecated aliases.
- Why prefer `layout.buildDirectory.dir(...)` over `"$buildDir/..."`?`buildDir`/`$buildDir` string interpolation is deprecated; `layout.buildDirectory` is the lazy `DirectoryProperty` that plays correctly with configuration cache and Provider wiring.
- What does `junitXml.mergeReruns = true` do?It collapses retried executions of the same test into one `<testcase>` with `<rerunFailure>`/`<flakyFailure>` children, so CI can flag flakiness instead of double-counting.
saying these in an interview costs you the question
- Using `enabled`/`destination` as the current API without noting they're deprecated aliases
- Hardcoding `$buildDir` paths instead of `layout.buildDirectory`
- Thinking disabling HTML also disables the XML — they're independent