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.
Related
I've got three modules:
amodule
bmodule
cmodule
There are integration-tests in all of these three modules. The bmodule is the base - it contains BaseClass, which I want to use in integration-tests in amodule and cmodule
The example of my project is on github: https://github.com/ljql/test-deps
I try to add dependency to integration-tests of bmodule in amodule and cmodule.
File build.gradle looks the same in modules amodule and cmodule
sourceSets {
intTest {
java {
srcDir 'src/test-integration/java'
}
resources {
srcDir 'src/test-integration/resources'
}
compileClasspath += rootProject.project('bmodule').sourceSets.intTest.output
runtimeClasspath += rootProject.project('bmodule').sourceSets.intTest.output
}
}
configurations {
intTestCompile {
extendsFrom compile
}
intTestRuntime {
extendsFrom runtime
}
}
dependencies {
intTestCompile project(path: ':bmodule', configuration: 'intTestCompile')
}
But when I try to build amodule I've got an error:
Build file '/test-deps/amodule/build.gradle' line: 10
A problem occurred evaluating project ':amodule'.
> Could not get unknown property 'intTest' for SourceSet container of type org.gradle.api.internal.tasks.DefaultSourceSetContainer.
The build of cmodule, which is the copy of amodule, is completed successfully.
The only difference between amodule and cmodule is in their names. The amodule precedes the bmodule and the cmodule followed by the bmodule
How can I specify a sequence of gradle resolve dependencies in my case? I want gradle to find out about the bmodule first and then about the amodule and the cmodule
Try putting this in the your amdoule's build.gradle
preBuild.dependsOn ":bmodule:build"
You have a project evaluation ordering problem. Since both amodule and cmodule expect to find specific components of bmodule, you need to ask Gradle to make sure bmodule has been evaluated first.
You can add in the build files of amodule and cmodule the following:
evaluationDependsOn(':bmodule')
While Gradle offers an API to resolve this, it will only work if you have no cycle, that is bmodule does not have dependencies on either amodule or cmodule, otherwise you will not be able to find a solution this way.
Another approach would be to leverage proper dependency resolution and declare that you want to share some test classes by having them exposed as test-fixtures for example.
Gradle has three build phases:
Initialization
Configuration
Execution
But the final result of resolving all dependencies appears only when gradle completes all three phases.
The error indicates a string in sourceSets - it is from the phase Configuration. At the start of this phase gradle knows nothing about any configurations in all modules. And gradle learns about the content of each module sequentially module-by-module. Of course when gradle considers the configuration of the amodule for the first time, it doesn't know anything about any configurations in the bmudule.
In the question I want to define compileClasspath and runtimeClasspath in confuguration :amodule:intTest. But there is nothing to stop me to define it in a custom task. Gradle will execute it on the third phase and the final compileClasspath and runtimeClasspath will contain the information from the bmodule:intTest
The result build.gradle of amodule:
sourceSets {
intTest {
java {
srcDir 'src/test-integration/java'
}
resources {
srcDir 'src/test-integration/resources'
}
}
}
configurations {
intTestCompile {
extendsFrom compile
}
intTestRuntime {
extendsFrom runtime
}
}
task intTest {
sourceSets.intTest.compileClasspath += rootProject.project('bmodule').sourceSets.intTest.output
sourceSets.intTest.runtimeClasspath += rootProject.project('bmodule').sourceSets.intTest.output
}
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
If you're looking for sample gradle protobuf project look here.
I'm having hard time with gradle and protobuf,
i want to create a simple gradle project that will take any proto files from default src/main/proto, src/test/proto and compile them to src/main/java, src/test/java accordingly, then pack that into a jar and publish to local repo.
Unfortunately i'm new to gradle and cant figure out how the original project is composed.
Here is my unfinished build.gradle file
apply plugin: 'java'
apply plugin: "com.google.protobuf"
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
}
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.protobuf:protobuf-java:3.0.0-beta-1'
}
sourceSets {
main {
proto {
srcDir 'src/main/proto'
}
java {
srcDir 'src/main/java'
}
}
test {
proto {
srcDir 'src/test/proto'
}
proto {
srcDir 'src/test/java'
}
}
}
protobuf {
// Configure the protoc executable
protoc {
// Download from repositories
artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3'
}
generateProtoTasks {
// all() returns the collection of all protoc tasks
all().each { task ->
// Here you can configure the task
}
// In addition to all(), you may get the task collection by various
// criteria:
// (Java only) returns tasks for a sourceSet
ofSourceSet('main')
}
}
After runing jar task we have this :
as you can see gradle builds both test and main protos to the same classes directory (red arrows), in the jar i can see both generated classes included (while tests should be skipped).
but the main problem is that I want to make compile proto files directly to appropriate source directories (blue arrows), after that ordinary build will do the correct thing... After all we need those classes in src to use them in business logic...
So we only need one task that compiles proto to appropriate src directory... nothing more.
src/main/proto to src/main/java
src/test/proto to src/test/java
The current project as it is is located here. Please help to configure this, i'm pretty sure lot of people will need it later...
If I don't misunderstand your question it's quite simple to solve. If you don't want to distinguish between your own and the generated sources you just have to add set the generatedFileBaseDir like this generateProtoTasks.generatedFilesBaseDir = 'src'
So the entire build file looks like:
// ...
protobuf {
// Configure the protoc executable
protoc {
// Download from repositories
artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3'
}
generateProtoTasks.generatedFilesBaseDir = 'src' // <- that line
generateProtoTasks {
// all() returns the collection of all protoc tasks
all().each { task ->
// Here you can configure the task
}
Than your folder looks like:
src/main/java/com/vach/tryout/AddressBookProtos.java
src/main/java/com/vach/tryout/protobuf/Main.java
BUT:
That might not be the best idea to mix generate with handcrafted source code. So my suggestion would be to generate the source code into an own directory like generatedSources and add this directory to the java sourceSet. The build file would look like this:
sourceSets {
main {
proto {
srcDir 'src/main/proto'
}
java {
// include self written and generated code
srcDirs 'src/main/java', 'generated-sources/main/java'
}
}
// remove the test configuration - at least in your example you don't have a special test proto file
}
protobuf {
// Configure the protoc executable
protoc {
// Download from repositories
artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3'
}
generateProtoTasks.generatedFilesBaseDir = 'generated-sources'
generateProtoTasks {
// all() returns the collection of all protoc tasks
all().each { task ->
// Here you can configure the task
}
// In addition to all(), you may get the task collection by various
// criteria:
// (Java only) returns tasks for a sourceSet
ofSourceSet('main')
}
}
Your directory will look like this
src/main/proto/dtos.proto
src/main/java/com/vach/tryout/protobuf/Main.java
generated-sources/main/java/com/vach/tryout/AddressBookProtos.java
A nice side effect is that you can ignore this generated-sources dir in your git configuration. That's always a good idea not to publish generated source code.
I've asked a related question here JOOQ class generation and gradle
In that question I'm trying to find the best way to do a multi-stage build including generating classes in a middle step. I've gone down the Option Two approach, and now find myself an impasse.
I have the following build.gradle file
apply plugin: 'java'
apply plugin: 'eclipse'
sourceSets
{
bootstrap
generated {
compileClasspath += bootstrap.output
}
main {
compileClasspath += bootstrap.output
compileClasspath += generated.output
}
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.jooq:jooq-codegen:3.5.0'
classpath 'postgresql:postgresql:9.1-901.jdbc4'
classpath project(":")
}
}
dependencies
{
compile 'org.jooq:jooq:3.5.0'
compile 'org.jooq:jooq-codegen:3.5.0'
compile 'org.apache.poi:poi:3.10.1'
compile 'com.google.guava:guava:18.0'
generatedCompile 'org.jooq:jooq:3.5.0'
generatedCompile 'org.jooq:jooq-codegen:3.5.0'
generatedCompile 'org.apache.poi:poi:3.10.1'
generatedCompile 'com.google.guava:guava:18.0'
bootstrapCompile 'org.jooq:jooq:3.5.0'
bootstrapCompile 'org.jooq:jooq-codegen:3.5.0'
bootstrapCompile 'org.apache.poi:poi:3.10.1'
bootstrapCompile 'com.google.guava:guava:18.0'
}
task generate << {
//Use JOOQ to generate classes, with the output going into the generated sourceSet
.withDirectory(file("src/generated/java").getAbsolutePath())
}
generatedClasses
{
dependsOn bootstrapClasses
dependsOn generate
}
jar
{
dependsOn generatedClasses
dependsOn bootstrapClasses
}
The structure is that
the bootstrap source set holds some core java classes that are required for the code generation, plus an sql file that will be used to pouplate a database
The generate task uses the classes and sql file in boostrap to generate classes
The generated source set holds the outputs of the generation task, and
The main source set holds what might be called the "normal" classes (i.e. the ones that make use of the database being described by the bootstrap and generated classes)
I have a couple of problems, which I can't untangle:
I seem to have to duplicate the dependencies for each source-set
When the jar file gets built, it only contains the classes generated from the main source set
I should note that the build as it stands above will successfully generate each of the source-sets.
Any help would be greatly appreciated.
O.K. I think I have found the answer to this question. There were two parts....
The first problem, having to specify the same dependencies multiple times, was fixed by adding this:
configurations {
generatedCompile {
extendsFrom compile
}
bootstrapCompile {
extendsFrom compile
}
}
The second problem, the jar file not having all the build artefacts, was fixed by changing the jar task to
jar
{
from sourceSets.generated.output
from sourceSets.bootstrap.output
dependsOn bootstrapClasses
dependsOn generatedClasses
}
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.