I would like to start by saying you should use Gradle Test Fixtures, at the time of your question this wasn't possible, but nowadays it works pretty well.
That said, this problem will still come up, for example if you want to share code between suites of JVM Test Suite plugin. The plugin solves a lot of problems out of the box; sadly not the duplicate sources one.
Below are three alternative solutions for the problem:
- Share
testFixtures
in test suites
- Copy the source files and compile them multiple times.
- Share the tests via testFixtures.
Share testFixtures
in test suites
The best solution, combining the two built-in Gradle plugins.
testing.suites.named<JvmTestSuite>("...").configure { // this: JvmTestSuite
dependencies {
implementation(project())
implementation(testFixtures(project()))
}
}
Copy the source files and compile them multiple times.
I had a similar problem here and solved it with a copy. Adopting it to your example above it would be something like:
val copyFunctionalTestSupportClasses by tasks.registering<Copy>() {
from("src/testSupport/java")
into("src/testFunctional-testSupport/java")
}
java.sourceSets.named("functionalTest").configure {
java.srcDir(copyFunctionalTestSupportClasses)
}
val copyIntegrationTestSupportClasses by tasks.registering<Copy>() {
from("src/testSupport/java")
into("src/testIntegration-testSupport/java")
}
java.sourceSets.named("integrationTest").configure {
java.srcDir(copyIntegrationTestSupportClasses)
}
java.sourceSets.named("test").configure {
// Leave it alone, use the original;
java.srcDir("src/testSupport/java")
// or do the same as the others to improve consistency between all 3 test types.
}
... yes, you're seeing it right, the srcDir
gets a TaskProvider
instance; it'll execute the task when necessary.
Share the tests via testFixtures.
This is more useful if you actually want to run shared tests in multiple tasks, which looking at your setup, you probably don't want. I'll put it here anyway in case someone is looking for it (me in the future ).
- Move the common tests to
testFixtures
- Configure the
Test
task with
....configure { // this: Test
(testClassesDirs as ConfigurableFileCollection)
.from(sourceSets["testFixtures"].output.classesDirs)
}
This will make the Gradle test runner scan the testFixtures classes for @Test
s. This is weird though, because the fixtures shouldn't contain tests.
The proper solution is this:
java {
registerFeature("sharedTests") {
usingSourceSet(sourceSets.create("sharedTest"))
disablePublication()
}
}
dependencies {
"sharedTestImplementation"(project)
"sharedTestImplementation"(testFixtures(project))
"sharedTestImplementation"(...) // any normal dependencies
}
....configure { // this: Test
// This adds the @Test classes from sharedTest to each configured Test class.
testClassesDirs = files(
testClassesDirs, // Keep original.
sourceSets["sharedTest"].output.classesDirs,
)
}
dependencies {
// Add a dependency on the new "feature" to the classpath of the Test task.
implementation(project) { // or project() for TestSuites
capabilities {
// Sadly an internal API need to be used,
requireCapability(org.gradle.internal.component.external.model.ProjectDerivedCapability.ProjectDerivedCapability(project, "sharedTests"))
// or, if you prefer, hard-code it:
requireCapability("${project.group}:${project.name}-shared-tests")
}
}
}