I’d love to use the new test suite feature to define a new test suite for my integration tests. Previously, I defined a new source set and defined the configuration manually:
However, I don’t know how I can inherit the libraries from my test task. For example, my tests depend on AssertJ, and I’d love to inherit this dependency. I don’t mind depending on the actual test implementation, too, although that’s not necessary.
I would go a different way. There’s no reason for testImplementation and integrationTestImplementation to have some kind of relationship. You need to know the names of the configurations and you have to drop out of the context of “testing” to define it.
Just explicitly say that “all JVM test suites have a dependency on XXX”:
Thank you, that looks much nicer! However, follow-up issue: in my integration tests I don’t have access to transitive dependencies from my project. However, strangely, this isn’t an issue with the default test task.
Project :project-b has a dependency implementation project(':project-a'). A class that is declared in src/main/java of :project-a can be used from a test in src/test/java in “project-b”. However, this class is not available from within the integrationTest in src/integrationTest/java in “project-b”.
According to this integration test, the “test” task automatically gets transitive dependencies which appear in testCompileClassPath. This does not happen for non-default tasks, even if the “implementation project” dependency is given explicitly.
Looks like I have to do that manually for integrationTest?
Yes, this is by design. The default test suite preserves existing behavior of the test task, custom test suites will require these deps to be specified explicitly. We were hoping to enable flexibility with this, as some custom suites may not require these transitive deps.
OK, thank you for the clarification. I guess there are good reasons for this. However, I’d love to have a shortcut to (explicitly) copy the behaviour that “test” has, maybe something like “implementation transitive(project)”.
Hey, I stepped across the same “problem”. Actually I have created a build file which uses the jvm-test-suite, but the dependencies coming from the project are actually not used, as the dependencies from the main project are not found in the step of compiling the integration test. Now I was asking myself how exactly I can make this happen.
Do you have a code snippet for me that should work? I am pretty new to Gradle and especially this plugin, but I would love to use it as probably this is the last step missing in my configuration.
I also recently ran into this problem, and I came up with a way to extend one test suite from another. The code below does exactly that. It is a little hacky in the way that it auto-discovers which configurations to extend with which ones, but it works. I am not sure how to determine which configurations belong to which test tasks without matching on the generated configuration names. This matches up the configuration names with the name of the test tasks removed.
I hope that the Gradle team will come up with a cleaner way to do this via the DSL.
In a util file in buildSrc:
import org.gradle.api.PolymorphicDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.plugins.jvm.JvmTestSuite
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.NamedDomainObjectContainerScope
import org.gradle.kotlin.dsl.RegisteringDomainObjectDelegateProviderWithTypeAndAction
import org.gradle.kotlin.dsl.invoke
import org.gradle.testing.base.TestSuite
private fun extendFromConfigs(
suiteToExtendFrom: JvmTestSuite,
reuseSrc: Boolean = false,
subAction: JvmTestSuite.() -> Unit
): JvmTestSuite.() -> Unit {
var extendFromProject: Project? = null
var extendFromConfigMap: Map<String, Configuration>? = null
var extendFromTask: Test? = null
suiteToExtendFrom.targets {
all {
extendFromTask = testTask.get()
extendFromProject = extendFromTask!!.project
extendFromConfigMap = configMapFor(extendFromProject!!, extendFromTask!!)
}
}
return {
useJUnitJupiter()
dependencies {
implementation(extendFromProject)
}
if (reuseSrc) {
sources.java.setSrcDirs(suiteToExtendFrom.sources.java.srcDirs)
sources.resources.srcDirs(suiteToExtendFrom.sources.resources.srcDirs)
}
targets {
all {
testTask {
val extendedConfigMap = configMapFor(this.project, this)
extendedConfigMap.forEach {(name, config) ->
config.extendsFrom(extendFromConfigMap!![name])
}
dependsOn(project.tasks.getByName("${extendFromTask!!.name}Classes"))
if (reuseSrc) {
this.testClassesDirs = extendFromTask!!.testClassesDirs
}
}
}
}
this.apply(subAction)
}
}
private fun configMapFor(project: Project, testTask: Test): Map<String, Configuration> {
val configMap = mutableMapOf<String, Configuration>()
project.configurations.forEach { config ->
if (config.name.startsWith(testTask.name)) {
configMap[config.name.removePrefix(testTask.name)] = config
}
}
return configMap
}
fun <T : TestSuite, C : PolymorphicDomainObjectContainer<T>> C.registeringExtended(
suiteToExtend: JvmTestSuite,
reuseSrc: Boolean = false,
action: JvmTestSuite.() -> Unit
): RegisteringDomainObjectDelegateProviderWithTypeAndAction<out C, JvmTestSuite> =
RegisteringDomainObjectDelegateProviderWithTypeAndAction.of(this, JvmTestSuite::class, extendFromConfigs(suiteToExtend, reuseSrc, action))
Then in build.gradle.kts, to run the same tests with different dependencies (registeringExtended gets “false” to not reuse the same test classes, but only share dependencies):
testing {
suites {
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}
val testOther by registeringExtended(test, true) {
dependencies {
implementation("other:depencency:1.0.0")
}
}
project.tasks.named("check") {
dependsOn(testOther)
}
}
}