Multi-project test dependencies with gradle

Solution 1:

Deprecated - For Gradle 5.6 and above use this answer.

In Project B, you just need to add a testCompile dependency:

dependencies {
  ...
  testCompile project(':A').sourceSets.test.output
}

Tested with Gradle 1.7.

Solution 2:

Simple way is to add explicit task dependency in ProjectB:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')

Difficult (but more clear) way is to create additional artifact configuration for ProjectA:

task myTestsJar(type: Jar) { 
  // pack whatever you need...
}

configurations {
  testArtifacts
}

artifacts {
   testArtifacts myTestsJar
}

and add the testCompile dependency for ProjectB

apply plugin: 'java'
dependencies {
  compile project(':ProjectA')
  testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}

Solution 3:

This is now supported as a first class feature in Gradle. Modules with java or java-library plugins can also include a java-test-fixtures plugin which exposes helper classes and resources to be consumed with testFixtures helper. Benefit of this approach against artifacts and classifiers are:

  • proper dependency management (implementation/api)
  • nice separation from test code (separate source set)
  • no need to filter out test classes to expose only utilities
  • maintained by Gradle

Example

:modul:one

modul/one/build.gradle

plugins {
  id "java-library" // or "java"
  id "java-test-fixtures"
}

modul/one/src/testFixtures/java/com/example/Helper.java

package com.example;
public class Helper {}

:modul:other

modul/other/build.gradle

plugins {
  id "java" // or "java-library"
}
dependencies {
  testImplementation(testFixtures(project(":modul:one")))
}

modul/other/src/test/java/com/example/other/SomeTest.java

package com.example.other;
import com.example.Helper;
public class SomeTest {
  @Test void f() {
    new Helper(); // used from :modul:one's testFixtures
  }
}

Further reading

For more info, see the documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures

It was added in 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects

Solution 4:

I know it's an old question but I just had the same problem and spent some time figuring out what is going on. I'm using Gradle 1.9. All changes should be in ProjectB's build.gradle

To use test classes from ProjectA in tests of ProjectB:

testCompile files(project(':ProjectA').sourceSets.test.output.classesDir)

To make sure that sourceSets property is available for ProjectA:

evaluationDependsOn(':ProjectA')

To make sure test classes from ProjectA are actually there, when you compile ProjectB:

compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')

Solution 5:

I've come across this problem myself recently, and man is this a tough issues to find answers for.

The mistake you are making is thinking that a project should export its test elements in the same way that it exports its primary artifacts and dependencies.

What I had a lot more success with personally was making a new project in Gradle. In your example, I would name it

Project A_Test -> src/main/java

I would put into the src/main/java the files that you currently have in Project A/src/test/java. Make any testCompile dependencies of your Project A compile dependencies of Project A_Test.

Then make Project A_Test a testCompile dependency of Project B.

It's not logical when you come at it from the perspective of the author of both projects, but I think it makes a lot of sense when you think about projects like junit and scalatest (and others. Even though those frameworks are testing-related, they are not considered part of the "test" targets within their own frameworks - they produce primary artifacts that other projects just happen to use within their test configuration. You just want to follow that same pattern.

Trying to do the other answers listed here did not work for me personally (using Gradle 1.9), but I've found that the pattern I describe here is a cleaner solution anyway.