Gradle multi project - sharing test code between modules - java

ProjectA contains an abstract unit test, TestA.
ProjectB has a test called TestB, which needs to extends from TestA, to fulfil the test requirements for this specific implementation.
I've added to the build.gradle configuration file on ProjectB, ProjectA as a dependency compilation test:
testCompile project(':ProjectA')
Also, as a dependency compilation:
compile project(':ProjectA')
Although I'm able to extend from TestA, when I try to run TestB I get the next error:
error: cannot find symbol class TestA
So, is there any way to share test code between modules?
Thanks.

As mentioned in this question you should add dependency on test sources like this:
compileTestJava.dependsOn tasks.getByPath(':projectA:testClasses')
testCompile files(project(':projectA').sourceSets.test.output.classesDir)

Related

Can't get test fixtures to work in gradle

I've been using these answers to try to get a test class to be visible from subproject ":A:B" to ":A:C":
(https://discuss.gradle.org/t/gradle-test-classes-needed-in-another-sub-project/13365/2)
(Multi-project test dependencies with gradle)
using plugin
Using any of these methods still gives me a java.lang.NoClassDefFoundError on Project B. I'm using gradle 7.2.
Is there another way to expose test fixtures in gradle?
You'd have to add a third module, which then can be referenced as testImplementation project().

Gradle test fixtures plugin and core module dependencies

I have a project built with Gradle version 6.4 and JDK 8. I'm trying to use the Gradle plugin for Test Fixtures (java-test-fixtures) but I have some issues with the dependencies.
According to the Gradle page linked above, the project should be structured like this:
core-module
-- src
-- main
-- java
-- test
-- java
-- testFixtures
-- java
While the build.gradle.kts file has the following dependencies section:
dependencies {
api("com.my.external.project:1.0")
// ... more API dependencies
testFixturesCompileOnly(project(":core-module"))
testFixturesApi("junit:junit:4.12")
// ... more test dependencies
}
Now, in IntelliJ (the IDE I'm using) classes in the testFixtures/java source folder see the classes in the main/java source folder. So I can add new Java classes under testFixtures/java that have dependencies on those under main.
However, I won't be able to import the dependencies from the external library com.my.external.project:1.0. The problem is confirmed when I try to run the Gradle task compileTestFixturesJava.
I can duplicate the entry in the dependencies section; e.g. I can add:
testFixturesImplementationOnly("com.my.external.project:1.0")
But that is not really what I expect to do; especially when I have dozens of dependencies.
I could also define the dependencies in an array and run a for-each over them. Still, this is not the cleanest solution.
Is there a clean solution that will allow the testFixtures module to use the dependencies declared in the main module?
Most important concept in the Gradle java-test-fixtures plugin is stated in their documentation:
[this plugin] will automatically create a testFixtures source set, in which you can write your test fixtures. Test fixtures are configured so that:
they can see the main source set classes
test sources can see the test fixtures classes
This plugin will indeed create the following dependencies: main <-- testFixtures , and testFixtures <-- test
In your case, testFixtures module should automatically depend on main sources, and also on main dependencies declared in api scope ( com.my.extenal.project:1.0)
See a similar example in a valid sample project here https://github.com/mricciuti/so-64133013 :
Simpsons class has access to Person class from main module
TestHelpers class has access to main dependencies declared in api configuration
Note that testFixtures will not inherit dependencies from the test module: if you need to use such libraries in this module (eg. JUnit, Mockito, ...) you will need to declare explicit dependency , using testFixturesImplementation or testFixturesApi configuration.
See example in core-module
plugins {
id ("java-library")
id ("java-test-fixtures")
}
dependencies {
// Main dependencies
// will be available in "testFixture" as well thanks to "api" configuration
api("org.apache.commons:commons-lang3:3.9")
api(project(":utils-module"))
// Testfixture dependencies
// ==> mockito lib will be available in testFixture module and also in consumer modules (e.g test)
testFixturesApi("org.mockito:mockito-core:3.5.13")
// Test dependencies
// dependencies specific to the "test" module; not visible from "testFixtures"
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.3.1")
}

Gradle Android "Duplicate class foo found in module bar" error

I have android module named A. I added dependency to another module B like so: implementation project(':B'). While the module A builds without an issue, when I try to build (and run) integration tests, I get
Duplicate class org.xmlpull.v1.XmlPullParser found in modules kxml2-2.3.0.jar (net.sf.kxml:kxml2:2.3.0) and xpp3-1.1.3.3.jar (xpp3:xpp3:1.1.3.3)
Duplicate class org.xmlpull.v1.XmlPullParserException found in modules kxml2-2.3.0.jar (net.sf.kxml:kxml2:2.3.0) and xpp3-1.1.3.3.jar (xpp3:xpp3:1.1.3.3)
Duplicate class org.xmlpull.v1.XmlPullParserFactory found in modules kxml2-2.3.0.jar (net.sf.kxml:kxml2:2.3.0) and xpp3-1.1.3.3.jar (xpp3:xpp3:1.1.3.3)
Duplicate class org.xmlpull.v1.XmlSerializer found in modules kxml2-2.3.0.jar (net.sf.kxml:kxml2:2.3.0) and xpp3-1.1.3.3.jar (xpp3:xpp3:1.1.3.3)
I tried to exclude org.xmlpull.kxml by changing my import statement to
implementation (project(':B')) {
exclude group: 'org.xmlpull.v1'
}
but the problem still exists. I have run out of ideas. Can anyone help?
In my case the solution was to exclude xpp3
implementation(':B') {
exclude module: 'xpp3'
}
here is a nice explanation

Gradle war - duplicated classes on classpath

I've got following gradle compile dependencies:
compile 'org.apache.cxf:cxf-rt-frontend-jaxws:3.1.6'
compile 'org.apache.cxf:cxf-rt-transports-http:3.1.6'
compile 'org.apache.cxf:cxf-rt-ws-security:3.1.6'
compile 'org.apache.cxf:cxf-common-utilities:2.5.11'
And after execute war task I've got ReflectionUtil class from both: org.apache.cxf:cxf-common-utilities:2.5.11 and org.apache.cxf:cxf-core:3.1.6
It results in noSuchMethodException because Spring uses wrong class.
How can I avoid it, and keep both dependencies?
I've experimented with different versions, etc. but without success.

Gradle: What is the difference between classpath and compile dependencies?

When adding dependencies to my project I am never sure what prefix I should give them, e.g. "classpath" or "compile".
For example, should my dependencies below be compile time or classpath?
Also, should this be in my applications build.gradle or in the module specific build.gradle?
Current build.gradle (at application level):
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.hibernate:hibernate-core:5.0.5.Final'
compile 'mysql:mysql-connector-java:5.1.38'
}
If buildscript itself needs something to run, use classpath.
If your project needs something to run, use compile.
The buildscript{} block is for the build.gradle itself.
For multi-project building, the top-level build file is for the root project, the specific build file is for sub-project (module).
Top-level build file where you can add configuration options common to all sub-projects/modules.
Do not place your application dependencies in top-level build file, they belong in the individual module build.gradle files
I'm going to guess that you're referencing compile and classpath within the dependencies {} block. If that is so, those are dependency Configurations.
A configuration is simply a named set of dependencies.
The compile configuration is created by the Java plugin. The classpath configuration is commonly seen in the buildScript {} block where one needs to declare dependencies for the build.gradle, itself (for plugins, perhaps).
If I understand correctly, you're confusing Project.dependencies script block with the Project.buildscript.dependencies script block (just like I did when I reached this question).
I'll try to answer this with what I found.
I think you should be already familiar with the Project.dependencies script block. In this block, we declare dependencies that are required by our source code. There are several ways to declare a dependency that we need for the project. See Gradle Tutorial: Dependency Types. I'll only mention the part that is the most relevant to this problem:
compile 'org.hibernate:hibernate-core:5.0.5.Final' is a module dependency declaration. The compile configuration (which is now deprecated by the implementation configuration.) is merely a keyword for Implementation only dependencies. It is not a keyword describing which type of dependency it is (by type here I'm following the three types defined in the tutorial, i.e. module, file, and project.)
In Gradle Tutorial: Organizing Build Logic it says:
If your build script needs to use external libraries, you can add them
to the script’s classpath in the build script itself. You do this
using the buildscript() method, passing in a closure which declares
the build script classpath.
This is the same way you declare, for example, the Java compilation
classpath. You can use any of the dependency types described in
Dependency Types, except project dependencies.
Having declared the build script classpath, you can use the classes in
your build script as you would any other classes on the classpath.
I hope things are getting clear to you now.
With classpath "com.android.tools.build:gradle:${Versions.android_gradle_plugin}" we're setting classpath method with com.android.tools.build:gradle:${Versions.android_gradle_plugin} which is a module dependency that is used by the build script itself rather than the source in your project.
On the other hand, with compile 'org.hibernate:hibernate-core:5.0.5.Final' we're declaring a module dependency required for your project with the compile configuration.
tl;dr: The classpath, compile, and implementation are all keywords that can be used against dependencies under different circumstances. The former is used when you want to pass in a dependency to the build script, and the latter is one of the configuration you may want to declare.
Android:
classpath in project build.gradle —— the implementation after classpath is only used by gradle it self, used in build script. So if i add the implementation (such as retrofit) in the project build.gradle classpath 'retrofit...', i can't get retrofit in my code!! Because —— my code can't see it, only the buildscript can see it.
implementation in app build.gradle —— add the implementation your code can use!!

Categories

Resources