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
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 from test, therefor we use the test.runtimeClasspath (which includes all dependenciess from test AND test itself) as classpath for the derived sourceSet:
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 since runtimeClasspath always expands output + runtimeSourceSet, don't get it
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 the java plugin use the test 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 when build or check 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 create sourceSet 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!
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:
nebula.facet
nebula.integtest
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()
}
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.
Related
I am trying to create a task in my build that will list all the runtimeClasspath dependencies before it builds the project. So basically, it should be the equivalent of the following command but for all the subprojects:
gradle dependencies --configuration runtimeClasspath
So far I have managed to come up with a task that lists all the dependencies:
subprojects {
task listDependencies(type: DependencyReportTask) {}
complileJava.dependsOn listDependencies
}
It works great in the sense that it lists all the dependencies of all the subprojects, but as mentioned above, it also lists a lot of stuff I don't need to see.
How do I limit the output of the above task to just runtimeClasspath?
Thanks!
Since you are defining a task of type DependencyReportTask, you can configure its configuration property, which will be the equivalent of the --configuration flag on the CLI.
So something like:
subprojects {
def listDeps = tasks.register("listDependencies", DependencyReportTask) {
setConfiguration("runtimeClasspath")
}
tasks.withType(JavaCompile).configureEach {
dependsOn(listDeps)
}
}
Note however that printing the runtimeClasspath before executing compileJava is a bit weird. The classpath used by the compile task will be compileClasspath.
Edited to use lazy task API as there is otherwise an ordering problem with plugin application
A Kotlin version of the above accepted answer:
val listDeps = tasks.register<DependencyReportTask>("listDependencies") {
setConfiguration("compileClasspath")
}
tasks.withType<JavaCompile> {
dependsOn(listDeps)
}
I'm using gradle. I only have one project, broken up into source files, then test files. My test code cannot see the source code. When the test code tries to compile, it just says that the source code packages are not present. What gives?
When running "gradle build", or "gradle test", I get the following:
What went wrong:
Execution failed for task ':compileTestJava'.
Compilation failed; see the compiler error output for details.
Sample compilation error (because there's hundreds of these)
/tst/ContactsTests.java:1: error: package HoaryGuts does not exist
import HoaryGuts.AlertsHandlers;
^
Gradle build.gradle file:
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'
repositories {
mavenCentral()
}
task getHomeDir << {
println gradle.gradleHomeDir
}
sourceSets {
source {
java {
srcDir "src"
}
resources {
srcDir "resources"
}
}
test {
java {
srcDir "tst"
}
compileClasspath = compileClasspath + configurations.compileOnly
}
}
test {
println "In the test function"
useTestNG()
testLogging {
exceptionFormat = 'full'
}
afterTest { desc, result ->
println "${desc.className} ${desc.name} ${result.resultType}"
}
outputs.upToDateWhen {false}
}
dependencies {
compile group: 'org.testng', name: 'testng', version: '6.9.10'
}
Just like maven, gradle uses main for production sources. These are normally defined under src/main/java for Java and test includes them just like it includes compile dependencies alongside of testCompile.
There is a reason for having src followed by a named configuration, followed by language: in your setup you do not provide a location for test resources which are very common and you make it more cumbersome to include other JVM languages such as Kotlin or Scala into the project. Maybe you do not need those, but there are rarely no good reasons to strongly deviate from the industry standard.
In any case, you did not move your main to a different location, you introduced a new source set source in addition to main (actually preserving main where they originally were). If you just specified new srcDir for main instead of introducing source (as you did for test) you would not need to extend compileClasspath and your main sources would be found in test.
If you do really want to stick to source rather than using main at a new location, in test you should add source compileClasspath and output to the classpath for test:
compileClasspath += sourceSets.source.compileClasspath + source.output + [whatever else you want to add]
But well, it is a poor choice.
I'm looking into migrating from maven to gradle, in this case, gradle itself seems to be working fine, but Idea isn't recognizing the source code that Immutables is generating.
I've read this blog post on APT, it's how I got this for.
/*
* This build file was generated by the Gradle 'init' task.
*
* This generated file contains a commented-out sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* user guide available at https://docs.gradle.org/4.3/userguide/tutorial_java_projects.html
*/
// Apply the java plugin to add support for Java
apply plugin: 'java-library'
apply plugin: 'idea'
buildscript {
repositories {
maven {
url 'https://d3vfm0n2cffdwd.cloudfront.net'
}
jcenter()
}
dependencies {
classpath 'io.spring.gradle:dependency-management-plugin:1.0.0.RELEASE'
}
}
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom 'com.xenoterracide:platform:0.1.39-SNAPSHOT'
}
}
repositories {
maven {
url 'https://d3vfm0n2cffdwd.cloudfront.net'
}
jcenter()
}
configurations {
apt
aptCompile
}
// In this section you declare the dependencies for your production and test code
dependencies {
implementation 'com.google.guava:guava'
aptCompile 'org.immutables:value'
compileOnly 'org.immutables:value'
apt 'org.immutables:builder'
// The production code uses the SLF4J logging API at compile time
implementation 'org.slf4j:slf4j-api'
// Declare the dependency for your favourite test framework you want to use in your tests.
// TestNG is also supported by the Gradle Test task. Just change the
// testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
// 'test.useTestNG()' to your build script.
testImplementation 'junit:junit'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.hamcrest:hamcrest-library'
}
compileJava {
options.annotationProcessorPath = configurations.aptCompile
}
for more code see bitbucket
one of the things that's getting me, is that depending on what I've tried the generated java either ends up in an out directory, or build on the classpath, of course so far neither of these solves the problem.
how do I fix this so that Idea can see the source for the generated types (so that it's not all highlighted in red)?
#CrazyCoder's links helped me get closer to a solution, this seems to resolve it though.
idea {
module {
sourceDirs += file("out/production/classes/generated")
}
}
I am new to Gradle. I would like to manipulate the following build.gradle contents to do this. Instead of separately running the tests then building the jar via separate commands, I'd like to do both in one command, except that the jar does not get created if one of the tests fail (it will not even try to build the jar).
apply plugin: 'java'
apply plugin: 'eclipse'
version = '1.0'
sourceCompatibility = 1.6
targetCompatibility = 1.6
// Create a single Jar with all dependencies
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.axa.openam'
}
baseName = project.name
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
// Get dependencies from Maven central repository
repositories {
mavenCentral()
}
test {
testLogging {
showStandardStreams = true
}
}
// Project dependencies
dependencies {
compile 'com.google.code.gson:gson:2.5'
testCompile 'junit:junit:4.12'
}
Thanks!
The simplest solution is to place all the tasks you want gradle to execute in order. So you may use the following:
gradle clean test jar
Tasks Breakout
clean: this is used mainly just to safely remove the last outdated jar (this is not mandatory);
test: execute the tests;
jar: create the jar artifact.
Key point: if one of the task fails for some reason gradle stops its execution.
So if just a single test fails for some reason an exception is thrown and the jar file is not created at all.
Alternative solution: add 'test' as dependency of 'jar'
Just to explore some other possibilities: modify the build.gralde file as follows:
[...]
jar {
dependsOn 'test'
[...]
}
[...]
Now every time you run gradle jar the test task is automatically executed before.
Emulate the pure command line solution using 'dependsOn'
To emulate the first command line approach (i.e., gradle clean test jar) using the dependency method you have to further modify the build.gradle. This is because is not assured that multiple dependsOn statements are evaluated in order:
[...]
jar {
dependsOn 'clean'
dependsOn 'test'
tasks.findByName('test').mustRunAfter 'clean'
[...]
}
[...]
Now you can use:
gradle jar
and both the tasks clean and test are executed (in the right order) before the actual jar task.
It is written in manual:
I.e. that src/sourceSet/java is a default path for "Java source for the given source set".
How to utilize this? Suppose I wish to create source set demo.
Can I write
sourceSets {
demo {
java {
srcDir 'src/demo/java'
}
resources {
srcDir 'src/demo/resources'
}
}
}
Can I write somehow without explicit paths?
May be I not required to write anything, just put files into demo subfolder?
UPDATE
I have tested
sourceSets {
demo {
java {
srcDir 'src/demo/java'
}
resources {
srcDir 'src/demo/resources'
}
}
}
and
sourceSets {
demo {
java
resources
}
}
and
sourceSets {
demo
}
In all cases running gradle build does not cause sources compiled.
build.gradle file is follows:
group 'net.inthemoon.tests'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.5
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}
sourceSets {
demo {
java {
srcDir 'src/demo/java'
}
resources {
srcDir 'src/demo/resources'
}
}
}
Sample project: https://github.com/dims12/MultipleSourceRoots
Gradle default tasks will not build non-default source sets unless there is an explicit dependency on them in the main chain. In order to compile your demo classes you'd have to call gradle demoClasses as per cricket_007's answer - or better yet, just do gradle build demo which will also put the generated classes in the target jar.
All you need is this :
sourceSets {
demo
}
... and the build demo task will indeed create any missing directory as expected, unless you have some weird permission scheme in effect.
Now I have looked at your git project and it seems your demo source set depends on the main classes. You need to make this dependency clear (i.e. add them to the demo classpaths) to avoid compilation errors :
sourceSets {
main
demo {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
This should be all you need:
sourceSets {
demo
}
More configuration may be needed to define the dependencies of these sources and where they should be used.
Brand new IntelliJ Java Gradle project. (Disclaimer: there was a setting to auto-create empty source directories automatically that I checked)
Upon adding this, it created that demo directory and the resources under it.
sourceSets {
demo
}
To compile your additional sourceSet you can run
gradlew demoClasses
As described in gradle's documentation and already mentioned in the other answers, to add a new source set it should be sufficient to write:
sourceSets {
demo {
compileClasspath += main.output
}
}
The Java plugin will automatically add corresponding compileDemoJava and processDemoResources tasks to your build.
To automatically execute these tasks during your regular build, you can add a dependency to the default tasks:
tasks.compileJava.dependsOn compileDemoJava
or the other way around:
tasks.compileJava.finalizedBy compileDemoJava
To add the output of your new source set to some distribution file, you will however need to define a corresponding task and declare this task's output to be an artifact of your build:
task demoJar(type: Jar) {
from sourceSets.demo.output
}
artifacts {
archives demoJar
}
See here for further reading on the publishing stuff
UPDATE:
Added main.output to compile classpath of demo source set as already suggested by #Amine