What do you need to publish and consume test fixtures of an external library, and what common pitfalls arise?
answer
- producer: java-test-fixtures + maven-publish
- must emit Gradle Module Metadata (.module)
- -test-fixtures classifier jar
- POM-only → 'no variant providing capability' error
- watch testFixturesApi transitive version clashes
basics
~20 sThe producer must apply java-test-fixtures, use maven-publish, and publish Gradle Module Metadata (the .module file) so the fixtures variant is described. Consumers then use testFixtures("g:a:v"). Common pitfalls: metadata not published, or the producer thinking a POM is enough.
solid answer
~40 s**Producer side:** apply `java-test-fixtures` (so the fixtures variant exists), publish with `maven-publish`, and ensure **Gradle Module Metadata** is emitted — it's on by default but can be disabled, and only it can describe the test-fixtures variant and its capability. The fixtures jar is published with a `-test-fixtures` classifier alongside the main artifact. **Consumer side:** declare `testImplementation(testFixtures("com.example:lib:1.0"))`. **Pitfalls:** (1) producer published only a POM → consumer hits 'no variant providing capability ...-test-fixtures'. (2) Metadata publication accidentally disabled. (3) Repository doesn't serve the `.module` file. (4) Forgetting that the consumer also gets the producer's `testFixturesApi` transitive deps — version conflicts can surface. (5) Treating fixtures as production code and accidentally shipping them. Because external consumption is a two-sided contract, it's most reliable inside one organization with consistent publishing conventions.
code
kotlin · 15 lines// Producer build.gradle.kts
plugins {
`java-library`
`java-test-fixtures`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"]) // includes the test-fixtures variant
}
}
}
// Gradle Module Metadata is generated and published by default.go deeper
Know that external fixtures are consumed with testFixtures('g:a:v') and need something extra beyond a normal POM.
Name Gradle Module Metadata as the requirement and the -test-fixtures classifier artifact.
Diagnose 'no variant providing capability' errors, watch transitive testFixturesApi clashes, and keep fixtures test-scoped.
Own a publishing convention that guarantees module metadata across internal libraries so fixtures reuse is reliable org-wide.
## The publishing contract External fixture sharing only works if both ends cooperate. ### Producer responsibilities 1. **Apply `java-test-fixtures`** so the fixtures variant and its `group:name-test-fixtures` capability exist. 2. **Publish via `maven-publish`.** The `java`/`java-library` components include the fixtures variant automatically. 3. **Emit Gradle Module Metadata.** Gradle writes a `*.module` JSON file next to the POM that enumerates every variant (main api/runtime, fixtures, javadoc, sources…) with attributes and capabilities. This is the *only* mechanism that can describe fixtures — a Maven POM has no slot for it. Module metadata is published by default; it can be turned off (`tasks.withType<GenerateModuleMetadata> { enabled = false }`), which silently breaks fixtures consumption. 4. The artifact lands as a jar with the **`test-fixtures` classifier**. ### Consumer responsibilities ```kotlin dependencies { testImplementation(testFixtures("com.example:lib:1.0")) } ``` Gradle reads the module metadata, finds the variant carrying the test-fixtures capability, and resolves its jar plus its `testFixturesApi` transitive dependencies. ## Common pitfalls - **POM-only publication.** The classic failure: producer applied the plugin but module metadata wasn't published (or the repo strips it). Consumer error: *Unable to find a variant of com.example:lib:1.0 providing the requested capability com.example:lib-test-fixtures*. - **Disabled metadata generation.** Some legacy setups disable `GenerateModuleMetadata`; that removes the variant description. - **Repository compatibility.** A repository or proxy must serve the `.module` file; some mirrors only cache the POM/jar. - **Transitive leakage of fixture deps.** Consumers inherit `testFixturesApi` deps; an old AssertJ pinned there can clash with the consumer's version. - **Accidental production shipment.** Don't add fixtures to the main component or declare them with `implementation` — keep them test-scoped. ## Practical guidance External fixtures are most reliable **within an organization** that controls its publishing pipeline. For open-source or cross-org consumption, verify the producer ships module metadata, or fall back to a conventional published test-support module if older consumers can't read metadata.
- A consumer reports 'Unable to find a variant ... providing capability lib-test-fixtures'. How do you diagnose it?Check the producer's repository for the `*.module` file. If only a `.pom` and jars are present, module metadata wasn't published (or was disabled / stripped by a mirror). Re-enable `GenerateModuleMetadata` and republish.
- Why is external fixture consumption more fragile than local project consumption?Locally, Gradle has the producer's full build model and resolves the variant directly. Externally it relies entirely on published Gradle Module Metadata; any gap in publishing or repository handling breaks resolution.
saying these in an interview costs you the question
- Saying a Maven POM alone is enough to consume external fixtures.
- Forgetting that disabling Gradle Module Metadata silently breaks fixtures publishing.