Transitive dependency issue with deployed artifacts using the new Java test fixtures plugin

Hello everybody.

I was very exited to discover that the latest Gradle version comes with a new Java test fixtures plugin.

I daily work on multi-project code bases. I previously created my own test fixtures source set but I certainly did not do it as well as this new plugin does.

I replaced my custom configuration by this new plugin and it globally worked very well and I would like to thank everybody that worked on it!

I just encountered an issue when using this plugin on projects that are published on Maven.

We have a set of libraries shared by multiple code bases. It is a multi-project build and each project is published as a Maven artifact in a Maven repository. To simplify, let’s say it contains two projects, shared-project-1 and shared-project-2. shared-project-2 depends on shared-project-1 (implementation dependency type).

In another code base, we have a dependency to shared-module-2 that is resolved via our internal Maven repository.

This worked as expected until I apply the Java test fixtures plugin to the shared projects. The project builds (the dependency to shared-project-2 is correctly resolved) but it fails at runtime because a class of the shared-project-1 cannot be resolved. After investigation, it appears that the classpath used at runtime or when running tests (same issue) does contain the shared-module-2 jar but neither the shared-module-1 jar nor its own dependencies.

When I don’t apply the Java test fixtures plugin to the shared build, the Maven pom of shared-project-2 simply contains a dependency to share-project-1 in runtime scope.

When I apply the plugin (which automatically publishes test fixtures with the test-fixtures classifier), the dependencies are a little more complex. It still contains the dependency to shared-project-1 in runtime scope, but it also contains an optional dependency to shared-project-1 in compile scope. I assume this second dependency is about the test-fixtures jar.

It sounds like when this optional dependency exists, it overrides the other ones defined to this same artifact and Gradle excludes it from the runtime scope (even if the dependency is explicitly declared to this artifact in scope runtime).

I tried to replace the dependency between shared-project-2 and shared-project-1 by an api one (even if I don’t need it). In this case, the pom contains two dependencies to the shared-module-1, one in compile scope and one optional in compile scope, but the result is the same.

Note that I don’t get this issue when skipping the Maven repository publication thanks to includeBuild. It probably confirms that the problem comes from the fact this artifact is published in a Maven repository (given that the Maven dependency model is less rich than the Gradle one ?).

I don’t know how to fix this issue. Is the published Maven pom correct? Is the Gradle behavior regarding optional dependencies right ? I don’t know enough the Maven model to answer these questions.

Can anyone help me?

Thanks in advance!

As explained in my previous post, I felt that the issue was coming from the fact the Maven model is too limited to describe this complex situation.

I just come to remember I read a few months ago a blog post regarding Gradle metadata which is aimed at fixing this issue by publishing an extra metadata file in Maven repositories with a higher precision.

I read the post again and it appears that I am exactly in the use case described in this article.

I tried to enable this experimental feature in the shared multi-project build and… it works!

Thanks to all the Gradle contributors for building such a great tool!