Skip to content

[MNG-1378] make test-jar dependencies transitive - #13066

Open
mcc0nnell wants to merge 1 commit into
apache:masterfrom
mcc0nnell:fix/MNG-1378-test-jar-transitives
Open

[MNG-1378] make test-jar dependencies transitive#13066
mcc0nnell wants to merge 1 commit into
apache:masterfrom
mcc0nnell:fix/MNG-1378-test-jar-transitives

Conversation

@mcc0nnell

@mcc0nnell mcc0nnell commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes MNG-1378 for Maven 4 by making a producer's direct test-scoped dependencies available when that producer is consumed as a dependency of type test-jar.

Maven's normal test scope remains non-transitive. The change is deliberately artifact-specific: the scope selector is wrapped with a parent-aware selector that permits test dependencies only while collecting the children of a test-jar artifact. Ordinary JAR dependencies keep the existing behavior.

Why

A test-jar is a separate artifact with its own classpath requirements. Its classes may depend on libraries declared with scope=test in the producer POM, but those libraries are currently dropped when another project consumes the test JAR.

As a result, consumers must manually duplicate dependencies that are already part of the producer's test classpath. This is the behavior reported by MNG-1378.

Implementation

The change adds a TestJarDependencySelector around Maven's existing scope selector.

The selector is parent-aware: when Resolver descends into a dependency whose artifact type is exactly test-jar, direct test-scoped children are allowed through. Everywhere else, dependency selection delegates unchanged to Maven's existing scope rules.

The implementation does not:

  • make Maven's test scope generally transitive;
  • rewrite producer POMs;
  • change ordinary JAR dependency behavior;
  • change Maven-3-personality behavior.

Compatibility

The behavior change applies only to Maven 4 semantics.

Maven-3-personality mode keeps the existing selector unchanged. This avoids changing long-established Maven 3 dependency behavior while allowing Maven 4 to give test-jar artifacts a dependency graph that matches their actual classpath requirements.

Tests

This PR adds a Core IT regression fixture with four modules:

Module Role
support An ordinary dependency
test-jar Produces a test JAR and depends on support with scope=test
consumer Depends only on the producer's test-jar and must receive both the test JAR and support
regular-consumer Depends on the producer's ordinary JAR and must not receive support

The producer artifacts are installed before the consumers are resolved separately, so the test exercises repository artifact-descriptor resolution rather than relying only on an in-reactor model.

Checklist

  • This pull request addresses one issue: MNG-1378.
  • The pull request description explains what changes, how, and why.
  • The branch contains one focused commit with a meaningful subject and body.
  • A behavioral Core IT regression fixture is included and is expected to fail without the runtime change.
  • mvn verify has been run successfully.
  • Core IT has been run successfully.

License

  • I hereby declare this contribution to be licenced under the Apache License Version 2.0, January 2004.

Draft until Maven CI and Core IT validation are complete.

Treat the direct test-scoped dependencies of a test-jar producer as part of that test artifact's dependency contract when resolving under Maven 4 semantics.

Keep ordinary test-scope behavior unchanged, preserve Maven 3 personality compatibility, and add an integration fixture that proves the test-jar consumer receives the producer's test dependency while a regular JAR consumer does not.

Signed-off-by: Robert McConnell <robert@mcc0nnell.org>
@mcc0nnell
mcc0nnell force-pushed the fix/MNG-1378-test-jar-transitives branch from 06e2472 to a022b55 Compare September 7, 2026 17:30
@mcc0nnell

Copy link
Copy Markdown
Contributor Author

@hboutemy, when you have a moment, I’d especially value your take on the semantics here. I kept this Maven 4-only and artifact-specific so ordinary test scope remains non-transitive; the key question is whether a consumed test-jar should carry the producer’s direct test-scoped dependencies as part of its dependency contract. If the direction is sound, I’m happy to adjust the implementation or tests to fit Maven’s preferred layer.

@gnodet

gnodet commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR — this addresses a long-standing gap (21 years, 71 votes, 5 duplicates).

On the direction: I think this is the right approach. The core insight is that a test-jar is not "private tests" — it's an explicitly published artifact with its own classpath contract. The moment you configure maven-jar-plugin with <goal>test-jar</goal>, you're making a deliberate decision to package test classes as a reusable, deployable artifact with its own GAV coordinates. At that point, its test-scoped dependencies are part of that contract, and stripping them produces a broken classpath for consumers.

The counter-argument that "tests are not public" only holds when there is no test-jar. If you don't want test code to be reusable, you simply don't create one. But once you do, the current behavior is the worst of both worlds: Maven lets you publish the artifact but silently drops the dependencies it needs to function, forcing every consumer to manually reduplicate them.

The approach here — a narrowly-scoped, Maven-4-only decorator that only allows direct test-scoped children through when resolving a test-jar node — is appropriately conservative. It doesn't make test scope generally transitive, it doesn't affect regular JAR resolution, and it preserves Maven 3 personality behavior.

A few things to address before this can move forward:

  1. Unit tests for TestJarDependencySelector — the IT coverage is solid, but the selector itself should have direct unit tests covering edge cases (nested test-jars, test-jar with transitive test-jar dependencies, interaction with exclusions/optional).
  2. Type detection — the selector keys on ArtifactProperties.TYPE being exactly "test-jar". Worth verifying this property is reliably set on artifacts resolved from the repository, not just in-reactor.

@gnodet

gnodet commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

One additional thought: this PR targets master (4.1.0), but the feature did not exist in 4.0.0. A test-jar consumer upgrading from 4.0.0 to 4.1.0 would suddenly get extra transitive dependencies they didn't have before — that could break builds (dependency conflicts, convergence enforcer rules, etc.). Even if the new behavior is correct, a silent behavioral change in a minor version can be painful.

I'd suggest adding a feature flag in o.a.m.api.feature.Features (with a corresponding constant in Constants), following the existing pattern (e.g. consumerPom). Something like:

public static boolean testJarTransitiveDeps(@Nullable Map<String, ?> userProperties) {
    return doGet(userProperties, Constants.MAVEN_TEST_JAR_TRANSITIVE_DEPS, !mavenMaven3Personality(userProperties));
}

This way it:

  • defaults to true in Maven 4 mode
  • defaults to false under Maven 3 personality
  • can be explicitly overridden via -Dmaven.testJarTransitiveDeps=false (or in .mvn/maven.config)

This gives users a safety valve if the new transitive deps break their build, and keeps the Maven 3 personality handling clean — just a different default for the same feature flag.

@gnodet gnodet added the enhancement New feature or request label Sep 7, 2026
@gnodet gnodet added this to the 4.1.0 milestone Sep 7, 2026
@elharo elharo changed the title MNG-1378: make test-jar dependencies transitive [MNG-1378] make test-jar dependencies transitive Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants