So I've got a multi-project gradle build consisting of:
myapp
myapp2
shared
testLib
Where myapp and myapp2 have compile dependencies on shared.
The testLib project also has a compile project dependency on shared. It exists to define some unit test helper code which uses shared classes. These classes live in its main sourceSet, as the purpose of this project is to build library containing test helper classes.
Now the shared project has unit tests. These tests utilize the helper code in testLib. Thus shared has a testCompile project dependency on testLib.
Gradle has no problem with this. It understands to first build shared, then build testLib, then run the unit tests in shared just fine. Buildship however, does not. It flags this as project dependency cycles after I import the build: "A cycle was detected in the build path of project 'shared'. The cycle consists of projects {shared, testLib}." and a similar message for the testLib project.
So why not just roll testLib into the test sourceSet of shared, you might ask? Well, the thing is, some of that unit test helper code is also used by the unit tests in myapp and myapp2. Both of these projects have compile project dependencies on shared and testCompile project dependencies on testLib.
Is there any way to get Buildship to understand that this is not really a project dependency cycle?
EDIT:
I have tried part of the solution here: https://softnoise.wordpress.com/2014/09/07/gradle-sub-project-test-dependencies-in-multi-project-builds/ changing my testLib build to this (after moving the test helper class back into the test sourceSet from main):
plugins {
id 'java'
}
configurations {
testOutput
}
dependencies {
compile project(':shared')
}
task jarTest (type: Jar) {
from sourceSets.test.output
classifier = 'test'
}
artifacts {
testOutput jarTest
}
and changing my shared project to reference the new testLib-test.jar via:
testCompile project(path: ':testLib', configuration: 'testOutput')
Still no luck. Once again gradle has no issues and builds fine, but after deleting and re-importing the project in eclipse, Buildship returns the same cycle warnings as before.
Is Buildship just currently unable to handle this kind of situation?
I guess this is happening because, eclipse has only a single classpath for the whole project (for both main and test). So where as gradle compile and testCompile as two different configurations - when importing into eclipse you will find issues.
You need to get rid of one of the dependencies. May be you can create a separate project for tests from testLib.
first, I have a project like this:
project-a
src
main
java
A.java
test
java
ATest.java
then, I have another project like this:
project-b
src
main
java
B.java
test
java
BTest.java
the build.gradle configuration, project-b dependence project-a
dependencies{
compile project(":project-a")
}
the question is BTest.java can access ATest.java, how to avoid this?
-------------------show more detail---------------
settings.gradle
rootProject.name = 'test-dependence'
include 'project-a', 'project-b'
project-b/build.gradle
dependencies {
compile project(":project-a")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
Unfortunately there's a bit of an impedence mismatch between Gradle modules and IntelliJ modules since Gradle allows multiple classpaths (configurations) in a module and IntelliJ has a single classpath per module.
Basically IntelliJ will allow BTest.java to access ATest.java but if you built from command line, Gradle won't allow it.
Try the following in intellij Gradle Settings.
Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle: check create separate modules per source set
Related question here
Well, I'm migrating my Android project to use the Clean Architecure:
https://github.com/android10/Android-CleanArchitecture
This means that part of my code is within the domain module (pure Java, no dependency with Android). For this project, I'm using Dagger 2, that generates source using the annotation processor (during compile time).
I have the following Gradle's configuration for my project:
apply plugin: 'java'
sourceCompatibility = 1.7
targetCompatibility = 1.7
configurations {
provided
}
sourceSets {
main {
compileClasspath += configurations.provided
runtimeClasspath += configurations.provided
}
test {
compileClasspath += configurations.provided
runtimeClasspath += configurations.provided
}
}
dependencies {
def domainDependencies = rootProject.ext.domainDependencies
def domainTestDependencies = rootProject.ext.domainTestDependencies
provided domainDependencies.daggerCompiler
provided domainDependencies.javaxAnnotation
compile domainDependencies.dagger
compile domainDependencies.rxJava
compile domainDependencies.joda
testCompile domainTestDependencies.junit
testCompile domainTestDependencies.assertJ
testCompile domainTestDependencies.mockito
testCompile domainTestDependencies.jMockLegacy
testCompile domainTestDependencies.commonsCsv
}
In my test source, I created the interface TestComponent and the Dagger is suposed to generate the DaggerTestComponent. When I try to build my project either through command line or Android Studio, I receive compilation errors of cannot find symbol and then: Execution failed for task ':domain:compileTestJava'.
I tried to change the 'provided' with 'compile' and 'testCompile'. It's still not working.
What is strange is that, after the failure of the compileTestJava, I can find the generated DaggerTestComponent.java in domain/build/classes/test. So, if it's being generated, why am I receiving this compile error?
It's important to note that this problem only happens in the test source. I have generated source of Dagger 2 being used in the main source.
UPDATE:
I commented every place that was trying to use the DaggerTestComponent and tried to build again. In the domain/build/classes/test, now I can find not only the DaggerTestComponent.java but also the .class resulted of the compilation. So, it's generating the source file and compiling it. Why is the compilation of files using it not working? It seems like some order problem, like the generated source isn't ready yet at the time of the compile of the other sources.
Thanks to #EpicPandaForce, I started worndering if there was a APT plugin for pure Java too. After searching, I found this one:
https://github.com/tbroyer/gradle-apt-plugin
I just applied that plugin and changed my dependencies with apt and testApt.
I am very new with Gradle and I would like to download all my test dependencies using Gradle. I used gradle init to generate my build file, and copied some dependencis from my previous scripts. However upon using gradle --refresh-dependencies in the project root, the test dependencies still do not download.
I tried searching for answers as to why this happens, but they don't seem to fix my issue. Is there something wrong with my build file?
Particularly, I am after downloading mockito and hamcrest below.
build file:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.slf4j:slf4j-api:1.7.5'
testCompile "junit:junit:4.11"
testCompile "org.mockito:mockito-core:1.9.5"
testCompile "org.hamcrest:hamcrest-library:1.3"
}
test {
testLogging {
events 'started', 'passed'
}
}
task wrapper(type: Wrapper) { gradleVersion = '1.11' }
I am also using Eclipse if that helps.
EDIT: After adding the Gradle nature to my project, it seemed to work. Can anyone explain why?
As far as I understand, --refresh-dependencies makes sure that the already cached dependencies are ignored and that dependencies are re-downloaded when they're needed.
Just use gradlew build, or gradlew test, and gradle will compile your tests, and thus download the missing dependencies.
It seemed to work now, after I added the Gradle nature to my project, then cleaning and regenerating eclipse files.
I have a multi-project configuration and I want to use gradle.
My projects are like this:
Project A
-> src/main/java
-> src/test/java
Project B
-> src/main/java (depends on src/main/java on Project A)
-> src/test/java (depends on src/test/java on Project A)
My Project B build.gradle file is like this:
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
}
The task compileJava work great but the compileTestJava does not compile the test file from Project A.
Deprecated - For Gradle 5.6 and above use this answer.
In Project B, you just need to add a testCompile dependency:
dependencies {
...
testCompile project(':A').sourceSets.test.output
}
Tested with Gradle 1.7.
This is now supported as a first class feature in Gradle. Modules with java or java-library plugins can also include a java-test-fixtures plugin which exposes helper classes and resources to be consumed with testFixtures helper. Benefit of this approach against artifacts and classifiers are:
proper dependency management (implementation/api)
nice separation from test code (separate source set)
no need to filter out test classes to expose only utilities
maintained by Gradle
Example
:modul:one
modul/one/build.gradle
plugins {
id "java-library" // or "java"
id "java-test-fixtures"
}
modul/one/src/testFixtures/java/com/example/Helper.java
package com.example;
public class Helper {}
:modul:other
modul/other/build.gradle
plugins {
id "java" // or "java-library"
}
dependencies {
testImplementation(testFixtures(project(":modul:one")))
}
modul/other/src/test/java/com/example/other/SomeTest.java
package com.example.other;
import com.example.Helper;
public class SomeTest {
#Test void f() {
new Helper(); // used from :modul:one's testFixtures
}
}
Further reading
For more info, see the documentation:
https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
It was added in 5.6:
https://docs.gradle.org/5.6/release-notes.html#test-fixtures-for-java-projects
Simple way is to add explicit task dependency in ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Difficult (but more clear) way is to create additional artifact configuration for ProjectA:
task myTestsJar(type: Jar) {
// pack whatever you need...
}
configurations {
testArtifacts
}
artifacts {
testArtifacts myTestsJar
}
and add the testCompile dependency for ProjectB
apply plugin: 'java'
dependencies {
compile project(':ProjectA')
testCompile project(path: ':ProjectA', configuration: 'testArtifacts')
}
I've come across this problem myself recently, and man is this a tough issues to find answers for.
The mistake you are making is thinking that a project should export its test elements in the same way that it exports its primary artifacts and dependencies.
What I had a lot more success with personally was making a new project in Gradle. In your example, I would name it
Project A_Test
-> src/main/java
I would put into the src/main/java the files that you currently have in Project A/src/test/java. Make any testCompile dependencies of your Project A compile dependencies of Project A_Test.
Then make Project A_Test a testCompile dependency of Project B.
It's not logical when you come at it from the perspective of the author of both projects, but I think it makes a lot of sense when you think about projects like junit and scalatest (and others. Even though those frameworks are testing-related, they are not considered part of the "test" targets within their own frameworks - they produce primary artifacts that other projects just happen to use within their test configuration. You just want to follow that same pattern.
Trying to do the other answers listed here did not work for me personally (using Gradle 1.9), but I've found that the pattern I describe here is a cleaner solution anyway.
I know it's an old question but I just had the same problem and spent some time figuring out what is going on. I'm using Gradle 1.9. All changes should be in ProjectB's build.gradle
To use test classes from ProjectA in tests of ProjectB:
testCompile files(project(':ProjectA').sourceSets.test.output.classesDir)
To make sure that sourceSets property is available for ProjectA:
evaluationDependsOn(':ProjectA')
To make sure test classes from ProjectA are actually there, when you compile ProjectB:
compileTestJava.dependsOn tasks.getByPath(':ProjectA:testClasses')
Please read the update bellow.
Similar problems described by JustACluelessNewbie occurs in IntelliJ IDEA. Problem is that dependency testCompile project(':core').sourceSets.test.output actually means: "depend on classes generated by gradle build task". So if you open clean project where classes are not generated yet IDEA won't recognise them and reports error.
To fix this problem you have to add a dependency on test source files next to dependency on compiled classes.
// First dependency is for IDEA
testCompileOnly files { project(':core').sourceSets.test.java.srcDirs }
// Second is for Gradle
testCompile project(':core').sourceSets.test.output
You can observe dependencies recognised by IDEA in Module Settings -> Dependencies (test scope).
Btw. this is not nice solution so refactoring is worth considering. Gradle itself does have special subproject containing test-support classes only. See https://docs.gradle.org/current/userguide/test_kit.html
Update 2016-06-05
More I am thinking about proposed solution less I like it. There are few problems with it:
It creates two dependencies in IDEA. One points to test sources another to compiled classes. And it is crucial in which order these dependencies are recognised by IDEA. You can play with it by changing dependency order in Module settings -> Dependencies tab.
By declaring these dependencies you are unnecessarily polluting dependency structure.
So what's the better solution? In my opinion it's creating new custom source set and putting shared classes into it. Actually authors of Gradle project did it by creating testFixtures source set.
To do it you just have to:
Create source set and add necessary configurations. Check this script plugin used in Gradle project: https://github.com/gradle/gradle/blob/v4.0.0/gradle/testFixtures.gradle
Declare proper dependency in dependent project:
dependencies {
testCompile project(path: ':module-with-shared-classes', configuration: 'testFixturesUsageCompile')
}
Import Gradle project to IDEA and use the "create separate module per source set" option while importing.
New testJar based (trnsitive dependancies supported) solution available as gradle plugin:
https://github.com/hauner/gradle-plugins/tree/master/jartest
https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
From documentation
In case you have a multi-project gradle build you may have test
dependencies between sub-projects (which probably is a hint that your
projects are not well structured).
For example assume a project where the sub-project Project B depends
on Project A and B does not only have a compile dependency on A but
also a test dependency. To compile and run the tests of B we need some
test helper classes from A.
By default gradle does not create a jar artifact from the test build
output of a project.
This plugin adds a testArchives configuration (based on testCompile)
and a jarTest task to create a jar from the test source set (with the
classifier test added to name of the jar). We can then depend in B on
the testArchives configuration of A (which will also include the
transitive dependencies of A).
In A we would add the plugin to build.gradle:
apply plugin: 'com.github.hauner.jarTest'
In B we reference the
testArchives configuration like this:
dependencies {
...
testCompile project (path: ':ProjectA', configuration: 'testArchives')
}
The Fesler's solution haven't worked for me, when i tried it to build an android project (gradle 2.2.0).
So i had to reference required classes manually :
android {
sourceSets {
androidTest {
java.srcDir project(':A').file("src/androidTest/java")
}
test {
java.srcDir project(':A').file("src/test/java")
}
}
}
Here if you are using Kotlin DSL, you should create your task like that according to Gradle documentation.
Like some previous answer, you need to create a special configuration inside the project that will share its tests class, so that you don't mix test and main classes.
Simple steps
In project A you would need to add in your build.gradle.kts :
configurations {
create("test")
}
tasks.register<Jar>("testArchive") {
archiveBaseName.set("ProjectA-test")
from(project.the<SourceSetContainer>()["test"].output)
}
artifacts {
add("test", tasks["testArchive"])
}
Then in your project B in the dependencies, you will need to add in your build.gradle.kts:
dependencies {
implementation(project(":ProjectA"))
testImplementation(project(":ProjectA", "test"))
}
I'm so late to the party (it is now Gradle v4.4) but for anyone else who finds this:
Assuming:
~/allProjects
|
|-/ProjectA/module-a/src/test/java
|
|-/ProjectB/module-b/src/test/java
Go to the build.gradle of project B (the one that needs some test classes from A) and add the following:
sourceSets {
String sharedTestDir = "${projectDir}"+'/module-b/src/test/java'
test {
java.srcDir sharedTestDir
}
}
or (assuming your project is named ProjectB)
sourceSets {
String sharedTestDir = project(':ProjectB').file("module-b/src/test/java")
test {
java.srcDir sharedTestDir
}
}
Voila!
Creating test-jar For Gradle 6.6.x
I know that there are many sources telling you, that is not OK, fe:
https://github.com/gradle/gradle/issues/11280
https://gradle.org/whats-new/gradle-6/#better-builds
But this is so damn simple and I just don't like the idea of having common test classes separately in testFixtures folder.
So in module A:
task jarTests(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests {
extendsFrom testRuntime
}
}
artifacts {
tests jarTests
}
And in module B:
testImplementation project(':moduleA')
testImplementation project(path: ':moduleA', configuration: 'tests')
And it just works!
If you want to use artifact dependencies to have:
ProjectB's source classes depend on Project A's source classes
ProjectB's test classes depend on Project A's test classes
then ProjectB's dependencies section in build.gradle should look something like this:
dependencies {
compile("com.example:projecta:1.0.0")
testCompile("com.example:projecta:1.0.0:tests")
}
For this to work ProjectA needs to build a -tests jar and include it in the artifacts it produces.
ProjectA's build.gradle should contain configuration like this:
task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
from sourceSets.test.output
}
configurations {
tests
}
artifacts {
tests testsJar
archives testsJar
}
jar.finalizedBy(testsJar)
When ProjectA's artifacts are published to your artifactory they will include a -tests jar.
The testCompile in ProjectB's dependencies section will bring in the classes in the -tests jar.
If you want to includeFlat ProjectA's source and test classes in ProjectB for development purposes then the dependencies section in ProjectB's build.gradle would look like this:
dependencies {
compile project(':projecta')
testCompile project(path: ':projecta', configuration: 'tests')
}
If you have mock dependencies which you need to share between tests, you can create new project projectA-mock and then add it as test dependency to ProjectA and ProjectB:
dependencies {
testCompile project(':projectA-mock')
}
This is clear solution to share mock dependencies, but if you need to run tests from ProjectA in ProjectB use other solution.
The solution mentioned by Nikita for Android + Kotlin looks like this:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Gradle for project that is going to use dependencies:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
If you are struggling to adapt the solution to the Gradle Kotlin DSL this is the equivalent:
configurations {
register("testClasses") {
extendsFrom(testImplementation.get())
}
}
val testJar = tasks.register<Jar>("testJar") {
archiveClassifier.set("test")
from(sourceSets.test)
}
artifacts.add("testClasses", testJar)
Some of the other answers caused errors one way or another - Gradle did not detect test classes from other projects or Eclipse project had invalid dependencies when imported. If anyone has the same problem, I suggest going with:
testCompile project(':core')
testCompile files(project(':core').sourceSets.test.output.classesDir)
The first line forces the Eclipse to link the other project as dependency, so all sources are included and up to date. The second allows Gradle to actually see the sources, while not causing any invalid dependency errors like testCompile project(':core').sourceSets.test.output does.
in project B:
dependencies {
testCompile project(':projectA').sourceSets.test.output
}
Seems to work in 1.7-rc-2