I want to add integration tests to my Gradle build (Version 1.0). They should run separately from my normal tests because they require a webapp to be deployed to localhost (they test that webapp). The tests should be able to use classes defined in my main source set. How do I make this happen?
Update for 2021:
A lot has changed in 8ish years. Gradle continues to be a great tool. Now there's a whole section in the docs dedicated to configuring Integration Tests. I recommend you read the docs now.
Original Answer:
This took me a while to figure out and the online resources weren't great. So I wanted to document my solution.
This is a simple gradle build script that has an intTest source set in addition to the main and test source sets:
apply plugin: "java"
sourceSets {
// Note that just declaring this sourceset creates two configurations.
intTest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
}
configurations {
intTestCompile.extendsFrom testCompile
intTestRuntime.extendsFrom testRuntime
}
task intTest(type:Test){
description = "Run integration tests (located in src/intTest/...)."
testClassesDir = project.sourceSets.intTest.output.classesDir
classpath = project.sourceSets.intTest.runtimeClasspath
}
Here is how I achieved this without using configurations{ }
.
apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_6
sourceSets {
integrationTest {
java {
srcDir 'src/integrationtest/java'
}
resources {
srcDir 'src/integrationtest/resources'
}
compileClasspath += sourceSets.main.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs Integration Tests"
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath += sourceSets.integrationTest.runtimeClasspath
}
Tested using: Gradle 1.4 and Gradle 1.6
java { srcDir 'src/integrationtest/java' } resources { srcDir 'src/integrationtest/resources' }
is not relevant since it just redeclares src/<sourceSetName>/...
to src/integrationtest/...
: here: change the capital T to a lower t –
Ripe compileClasspath += sourceSets.main.runtimeClasspath
is combining two sets of files. There is no usual conflict resolution for dependencies. You can end up with two versions of the same library. Extending configurations will help with that. –
Ephemeron This was once written for Gradle 2.x / 3.x in 2016 and is far outdated!! Please have a look at the documented solutions in Gradle 4 and up
To sum up both old answers (get best and minimum viable of both worlds):
some warm words first:
first, we need to define the
sourceSet
:sourceSets { integrationTest }
next we expand the
sourceSet
fromtest
, therefor we use thetest.runtimeClasspath
(which includes all dependenciess fromtest
ANDtest
itself) as classpath for the derivedsourceSet
:sourceSets { integrationTest { compileClasspath += sourceSets.test.runtimeClasspath runtimeClasspath += sourceSets.test.runtimeClasspath // ***) } }
- note) somehow this redeclaration / extend for
sourceSets.integrationTest.runtimeClasspath
is needed, but should be irrelevant sinceruntimeClasspath
always expandsoutput + runtimeSourceSet
, don't get it
- note) somehow this redeclaration / extend for
we define a dedicated task for just running integration tests:
task integrationTest(type: Test) { }
Configure the
integrationTest
test classes and classpaths use. The defaults from thejava
plugin use thetest
sourceSet
task integrationTest(type: Test) { testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath }
(optional) auto run after test
integrationTest.dependsOn test
(optional) add dependency from
check
(so it always runs whenbuild
orcheck
are executed)tasks.check.dependsOn(tasks.integrationTest)
(optional) add java,resources to the
sourceSet
to support auto-detection and create these "partials" in your IDE. i.e. IntelliJ IDEA will auto createsourceSet
directories java and resources for each set if it doesn't exist:sourceSets { integrationTest { java resources } }
tl;dr
apply plugin: 'java'
// apply the runtimeClasspath from "test" sourceSet to the new one
// to include any needed assets: test, main, test-dependencies and main-dependencies
sourceSets {
integrationTest {
// not necessary but nice for IDEa's
java
resources
compileClasspath += sourceSets.test.runtimeClasspath
// somehow this redeclaration is needed, but should be irrelevant
// since runtimeClasspath always expands compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
// define custom test task for running integration tests
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
tasks.integrationTest.dependsOn(tasks.test)
referring to:
- gradle java chapter 45.7.1. Source set properties
- gradle java chapter 45.7.3. Some source set examples
Unfortunatly, the example code on github.com/gradle/gradle/subprojects/docs/src/samples/java/customizedLayout/build.gradle or …/gradle/…/withIntegrationTests/build.gradle seems not to handle this or has a different / more complex / for me no clearer solution anyway!
compileTestJava
–
Ripe classesDir
was migrated to classesDirs
on gradle 5 –
Hermon The nebula-facet plugin eliminates the boilerplate:
apply plugin: 'nebula.facet'
facets {
integrationTest {
parentSourceSet = 'test'
}
}
For integration tests specifically, even this is done for you, just apply:
apply plugin: 'nebula.integtest'
The Gradle plugin portal links for each are:
If you're using
- Gradle 5.x, have a look at Documentation Section "Testing Java > Configuring integration tests
Example 14 and 15 for details (both for Groovy and Kotlin DSL, either which one you prefer)
- alt: "current" Gradle doc link at 2, but might defer in future, you should have a look at if examples changes)
- for Gradle 4 have a look at ancient version 3 which is close near to what @Spina posted in 2012
To get IntelliJ to recognize custom sourceset as test sources root:
plugin {
idea
}
idea {
module {
testSourceDirs = testSourceDirs + sourceSets["intTest"].allJava.srcDirs
testResourceDirs = testResourceDirs + sourceSets["intTest"].resources.srcDirs
}
}
Here's what works for me as of Gradle 4.0.
sourceSets {
integrationTest {
compileClasspath += sourceSets.test.compileClasspath
runtimeClasspath += sourceSets.test.runtimeClasspath
}
}
task integrationTest(type: Test) {
description = "Runs the integration tests."
group = 'verification'
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
As of version 4.0, Gradle now uses separate classes directories for each language in a source set. So if your build script uses sourceSets.integrationTest.output.classesDir
, you'll see the following deprecation warning.
Gradle now uses separate output directories for each JVM language, but this build assumes a single directory for all classes from a source set. This behaviour has been deprecated and is scheduled to be removed in Gradle 5.0
To get rid of this warning, just switch to sourceSets.integrationTest.output.classesDirs
instead. For more information, see the Gradle 4.0 release notes.
I gather the documentation wasn't great back in 2012 when this question was asked, but for anyone reading this in 2020+: There's now a whole section in the docs about how to add a source set for integration tests. You really should read it instead of copy/pasting code snippets here and banging your head against the wall trying to figure out why an answer from 2012-2016 doesn't quite work.
The answer is most likely simple but more nuanced than you may think, and the exact code you'll need is likely to be different from the code I'll need. For example, do you want your integration tests to use the same dependencies as your unit tests?
You might also want to configure your tests to run with jUnit:
task integrationTest(type: Test) {
...
useJunitPlatform()
}
Here is the solution for Kotlin (build.gradle.kts):
sourceSets {
create("uiTest") {
// Adds files from the main source set to the compilation and runtime classpaths of this new source set
// sourceSets.main.output is a collection of all the directories containing compiled main classes and resources
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
}
// Makes the uiTestImplementation configuration extend from testImplementaion,
// which means that all the declared dependencies of the test code (and transitively the main as well)
// also become dependencies of this new source set
val uiTestImplementation by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
val uiTestRuntimeOnly by configurations.getting {
extendsFrom(configurations.testRuntimeOnly.get())
}
Bonus:
val uiTest = task<Test>("uiTest") {
description = "Runs UI tests."
group = "verification"
testClassesDirs = sourceSets["uiTest"].output.classesDirs
classpath = sourceSets["uiTest"].runtimeClasspath
testLogging {
events(TestLogEvent.PASSED)
}
}
tasks.check { dependsOn(uiTest) }
I'm new to Gradle, using Gradle 6.0.1 JUnit 4.12. Here's what I came up with to solve this problem.
apply plugin: 'java'
repositories { jcenter() }
dependencies {
testImplementation 'junit:junit:4.12'
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
test {
java {
srcDirs = ['tests']
}
}
}
Notice that the main source and test source is referenced separately, one under main
and one under test
.
The testImplementation
item under dependencies
is only used for compiling the source in test
. If your main code actually had a dependency on JUnit, then you would also specify implementation
under dependencies
.
I had to specify the repositories
section to get this to work, I doubt that is the best/only way.
© 2022 - 2024 — McMap. All rights reserved.
java/withIntegrationTests
sample in the full Gradle distribution. – Combination