Test classes in Groovy don't see test classes in Kotlin - gradle

I have gradle project with Kotlin plugin.
In my project I uses groovy and Spock for tests.
One of utility classes used in tests in written in Kotlin and I put it to src/test/kotlin
I'm trying to use this class from groovy tests (Spock specification), I see that "compileTestKotlin" task runs first and compiles my utility class, but still "compileTestGroovy" fails, because it does not see it.
How can I fix this situation?
How to add build/classes/kotlin/test/ to the compilation classpath of groovy tests?

The issue is that by default compileTestGroovy doesn't include build/classes/kotlin/test folder, so your Kotlin util class cannot be seen from Groovy tests.
In order to fix it you can manually add Kotlin test sources to compileTestGroovy's classpath. Add the following to your build.gradle:
compileTestGroovy.classpath += files(compileTestKotlin.destinationDir)
// in more recent versions it must be
compileTestGroovy.classpath += files(compileTestKotlin.destinationDirectory)
If your build file is build.gradle.kts, add the following
// make groovy test code depend on kotlin test code
tasks.named<GroovyCompile>("compileTestGroovy") {
classpath += files(tasks.compileTestKotlin)
}

Related

calling Karate framework from Rest service or spring boot [duplicate]

I am using karate 0.9.2 with gradle. My project requires to have all karate tests inside src/main/java. So I configured the gradle dependency as ‘compile’ instead of ‘testCompile’ and also modified the sourceSets to point to main instead of test. When I ran my runner class with above configuration I got empty test suite message.
build.gradle snippet:
compile 'com.intuit.karate:karate-junit4:0.9.3'
compile 'com.intuit.karate:karate-apache:0.9.3'
sourceSets {
test {
resources {
srcDir file('src/main/java')
exclude '**/*.java'
}
}
}
Additionally, I have is to run the karate tests from the deployable project jar. Please point me the resources I can refer to achieve the same.
Not something we directly support but teams have done this in Spring Boot etc. It should be possible, see if this thread helps: https://github.com/intuit/karate/issues/520
Also you may not need JUnit also: https://github.com/intuit/karate/issues/427
And see the sample project in this ticket as an example: https://github.com/intuit/karate/issues/529
EDIT - in 1.0 onwards we hope that class-loading from spring-boot JAR files is more reliable: https://github.com/intuit/karate/issues/751

In a multi-module project can Gradle build a plugin as one module and then use that plugin in the same build?

We have a Gradle project with a bunch of modules. One of those modules is a custom code generator, written as a Gradle plugin. We want to run that code-generator plugin in another module later in the same overall multi-module build, in order to test the code generator.
We know how to create a separate project on the fly and run the code generator in that, but we need to run the code generator in the main project, not in a temporary test project.
Nothing we have tried works, and the Gradle documentation doesn't appear to address this. It seems to be fundamental to Gradle's design, because the entire set of plugins used in a build is basically a single program, assembled at the start. Trying to add a just-now-built plugin after the fact seems unsupported, or we're missing something.
The best we've been able to come up with so far is to implement the plugin in Java (Kotlin would also have worked), so the Gradle plugin is just a thin Gradle skin over the implementation, and call the Java implementation directly when running the code generator in the other module. This works, but it means we aren't actually testing the Gradle portion of the code generator.
This is natively supported in Maven (maven multi-module project with one plugin module, and https://maven.apache.org/guides/mini/guide-multiple-modules.html), which is not surprising because every plugin in Maven runs in a separate class loader. If it's not possible in Gradle, that would be one of the few cases where Gradle doesn't have feature parity.
A hacky way to do this is to run the newly-compiled plugin via Gradle's test kit runner.
A cleaner way to do this is to write plugins as thin shells of code written to Gradle's API that delegate the real work to plain old Java (or Kotlin) utility methods. This has a number of advantages:
You can unit test the utility methods.
You can use the utility methods for other purposes unrelated to the plugin.
You can call the utility methods directly from other modules in the project, thereby accomplishing what the plugin would have done if you could have built it and then called it in the same build.
To expand on the above answer.
Instead of calling the plugin like a plugin, add a main method that accepts the same parameters that Gradle plugin configuration passed to the plugin.
Then call the plugin's main using Gradle's Java exec task:
task(generateFoo, type: JavaExec) {
main = 'com.bar.Foo'
classpath = configurations.runtimeClasspath
args = ["arg1", "${projectDir}/src/generated/java"]
}
Note the args: those are the same pieces of information that used to be passed in via Gradle configuration:
apply plugin: 'foo-plugin'
generateFoo {
theArg "arg1"
outputDir "${projectDir}/src/generated/java"
}
Because the runtime classpath used by Java exec is the one for the calling module, you may encounter runtime classloader problems.
If that happens, it's easily fixed. Just change the rewritten plugin to a fat jar:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Foo Fat JAR', 'Main-Class': 'com.bar.Foo'
}
baseName = project.name + '-exec'
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
artifacts {
archives fatJar
}
And then execute the fat jar with Java exec:
def fooGenerate = task(generateFoo, type: JavaExec) {
main = 'com.bar.Foo'
classpath = files("${projectDir}/../foo-plugin-module/build/libs/foo-plugin-module-exec.jar")
args = ["arg1", "${projectDir}/src/generated/java"]
}
Finally, make the dependent module's compile task depend on the code generation:
compileJava.mustRunAfter fooGenerate
If you use the fatJar approach, you don't even need to declare implementation project(":foo") in the dependent modules.
It might be also be possible to use Gradle's composite builds for this (https://docs.gradle.org/current/userguide/composite_builds.html).

Specifying xmlpathinjar TestNG option with Gradle test task

Is it possible to run a TestNG test suite that is embedded in a JAR file via a Gradle test task?
My project includes JARed bundles of TestNG tests that have an embedded testng.xml file defining which tests should be run in the JAR. Is it possible for Gradle to refer to this embedded XML when running the TestNG tests?
From the command line I use the xmlpathinjar option.
I dont think it can be done using the Gradle TestNG task. I couldn't find any such support in the TestNGOptions
Instead of using
test{
useTestNG()
}
you could try going through this post on SO How can I tell Gradle to use my testng.xml file for Test Classes and Ordering? and maybe employ the approach detailed here https://stackoverflow.com/a/28868416
But when you are using a custom Gradle task to run your TestNG tests, please make sure that you add a reference to the ExitCodeListener
Here's a sample
task ('myTask', type: JavaExec) {
main = 'org.testng.TestNG'
classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath
args = ["-xmlpathinjar", "suites/mysuite.xml", "-listener", "org.testng.TestNG\$ExitCodeListener"]
}
More details on why the ExitCodeListener needs to be referred, can be found here

gradle fails to compile java classes using kotlin class

I have a gradle 4.1 multiproject containing a "projectA" containing 2 subfolders "api" and "implementation".
The multiproject uses kotlin and java-library plugins defined in the subprojects section of the main build.gradle.
The implementation project avec a API dependency to :projectA:api
In the api folder I have kotlin and java files inside 'src/main/java' and in the implementation project I'm creating a new instance of a kotlin class from the API.
Inside Intellij Idea, I don't have any compilation errors ; but when I compile the whole project using gradle I have an error: cannot find symbol. It is as if the compileJava doesn't have access to the folder kotlin-classes.
Inside the build/kotlin-classes, I see my file.class
The class file is on build/classes dir also
Details of the error :
Task :projectA:api:compileKotlin
Using kotlin incremental compilation
Task :projectA:implementation:compileJava
(...) error: cannot find symbol (the import fails)
Update 1 : removing java-library solved my problem
This is a known issue of the java-library plugin: when used in a project with another JVM language (Kotlin, Scala, Groovy etc.) , it does not register the classes of the other language so that the dependent projects get them as they consume the classes.
Fortunately, it has a workaround as well. Adapted to Kotlin, it would look like:
configurations {
apiElements {
outgoing.variants.getByName('classes').artifact(
file: compileKotlin.destinationDir,
type: ArtifactTypeDefinition.JVM_CLASS_DIRECTORY,
builtBy: compileKotlin)
}
}
If you use Kapt1, it's file: compileKotlinAfterJava.destinationDir, and for Gradle versions lower than 4.0 use builtBy: copyMainKotlinClasses instead.
This issue is also tracked in the Kotlin issue tracker: KT-18497, follow that issue to see when it's fixed on the Kotlin Gradle plugin side, so that the above workaround will be no more necessary.

How to get source code directory in Gradle plugin?

I want to get the directory of Java files in a Groovy plugin.
For example, I have a Java file in a directory:
"/gradleProject/src/main/java/com/file.java"
How can I get:
"src/main/java"
In Maven there is Build.getSourceDirectory(), what is the equivalent in Gradle?
Further to Rene's answer, the groovy DSL makes it easy to get the SourceSet
SourceSet mainSourceSet = project.sourceSets.main
In java this is a little bit more verbose
SourceSet mainSourceSet = project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().getByName("main");
In Gradle, those source folders are managed by SourceSets, that are brought to you by the Java plugin. The java plugin adds two sourceSets named main and test.
once the java plugin is applied, you can access those sourceSets and their properties (e.g. the folders you're looking) simply by name: project.sourceSets.main.srcDirs - this will give you all the configured source directories for the main sourceSet in your project.
The latest way to do this, using the Kotlin DSL:
val javaExt = project.extensions.getByType(JavaPluginExtension::class.java)
val mainJavaSource = javaExt.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
Using the Kotlin DSL (Gradle 7.5)
tasks.register<JacocoReport>("jacocoTestReport") {
dependsOn("testDebugUnitTest")
val sourceSets = project.extensions.getByType<KotlinProjectExtension>().sourceSets
sourceDirectories.setFrom(sourceSets)
}
Note that besides the JavaPluginExtension there is also a KotlinAndroidProjectExtension, so you may can get rid of the Android Gradle Plugin in your community plugins.

Resources