Separate Gradle source set for integration tests using Kotlin DSL - gradle

I'm working on a Spring Boot application implemented in Kotlin, and would like to migrate the Gradle build to use the Gradle Kotlin DSL.
The one thing I cannot figure out is how to set up a separate source set and task for my integration tests.
My source tree looks like this:
src
├── integrationTest
│ ├── kotlin
│ └── resources
├── main
│ ├── kotlin
│ └── resources
└── test
├── kotlin
└── resources
And the source set and task are set up like this with Gradle's Groovy DSL:
// build.gradle
sourceSets {
integrationTest {
kotlin {
compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
I've found many examples for using the Gradle Kotlin DSL, and for additional source sets - but nothing for the combination.
Can anyone help?

Here's how you can translate the Groovy script to the Kotlin DSL:
java {
sourceSets {
val integrationTest by creating {
kotlin.apply {
compileClasspath += sourceSets["main"].output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
}
configurations["integrationTestCompile"].extendsFrom(configurations["testCompile"])
configurations["integrationTestRuntime"].extendsFrom(configurations["testRuntime"])
val integrationTest by tasks.creating(Test::class) {
val integrationTestSourceSet = java.sourceSets["integrationTest"]
testClassesDirs = integrationTestSourceSet.output.classesDirs
classpath = integrationTestSourceSet.runtimeClasspath
}
Also see: the Migrating build logic from Groovy to Kotlin guide by Gradle

Related

Spring boot multi project with gradle generates the same jar

I'm new to gradle and I'm having trouble generating multiple jars with spring boot.
I generate two different builds, but when I run build A or build B, both are A
My project has the following structure:
root
├── facade
├─── rest-api
└─── web-api
├── dependencies
├─── services
├─── entities
└─── ...
├── settings.gradle
└── build.gradle
My intention is to have a mono repo of micro services. The micro services I will generate are the web api and rest api modules of the facade directory. These modules have dependencies of the module called dependencies as its name indicates.
As I described before, when I run the web api module, it's like I'm running rest api, even asking for its dependencies.
My settings.gradle:
rootProject.name = "root"
include ":root:facade:rest-api"
include ":root:facade:web-api"
include ":root:dependencies:entities"
include ":root:dependencies:services"
...
And my build.gradle
buildscript {
ext.kotlin_version = '1.3.61'
ext.spring_boot_version = '2.2.2.RELEASE'
ext.jjwt_version = '0.10.6'
ext.klockVersion = "1.7.3"
ext.queryDslVersion = '4.1.4'
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
}
}
group 'org.com'
version '0.1.0'
def javaProjects() {
return subprojects.findAll { new File(it.projectDir, "src").exists() }
}
subprojects {
repositories {
jcenter()
mavenCentral()
}
configure(javaProjects()) {
apply plugin: "java"
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
apply plugin: 'kotlin'
apply plugin: "kotlin-spring"
apply plugin: "kotlin-jpa"
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "io.jsonwebtoken:jjwt-api:$jjwt_version"
implementation "io.jsonwebtoken:jjwt-impl:$jjwt_version"
implementation "io.jsonwebtoken:jjwt-jackson:$jjwt_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.soywiz.korlibs.klock:klock-jvm:1.7.3"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation "org.springframework.boot:spring-boot-starter-web"
implementation("org.springframework.boot:spring-boot-starter-security")
implementation "org.postgresql:postgresql:42.1.3"
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation "khttp:khttp:1.0.0"
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude module: 'junit'
exclude module: 'mockito-core'
}
testImplementation('org.junit.jupiter:junit-jupiter:5.5.2')
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation('com.ninja-squad:springmockk:2.0.0')
}
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
}
}
project(":root:dependencies:services") {
bootJar {
enabled = false
}
jar {
enabled = true
}
}
project(":root:dependencies:entities") {
bootJar {
enabled = false
}
jar {
enabled = true
}
}
...
I feel that I must have something wrong with the build.gradle, but I don't understand what.
I also have omitted the build.gradle files of the modules rest api and web api since I only have the dependencies and I have not considered it relevant.
I had previously worked with maven and followed this architecture. I don't know if gradle is the right thing to do, so I'm open to any advice you can give me.
Thank you for your attention.
As I mentioned in the comments, the correct way to include subprojects is to replace the path to the subproject with : i.e. if the subproject is in (from the root project) sub/project1, then the correct way to include it is:
include ':sub:project1'
Now as for your other question in the comments regarding:
Main class name has not been configured and it could not be resolved
You can simply do:
mainClassName = 'full.cannonical.name.of.MainClass'
If all the projects have a main class, then you need to do this in each project's build.gradle

How can I configure the Jib Extension inside a buildSrc kotlin file?

I'm trying to modularize my build.gradle.kts. It was suggested to me to create a buildSrc folder.
After some research and som asking I found this article I hated Gradle!... so this was my try:
buildSrc tree:
buildSrc/
├── build.gradle.kts
├── settings.gradle.kts
└── src
└── main
├── kotlin
│   ├── Docker.kt
│   ├── MyProjectExtensions.kt
│   └── Versions.kt
└── resources
└── META-INF
└── gradle-plugins
└── pt.branden.brandenportal.properties
My build.gradle.kts:
plugins {
`kotlin-dsl`
id("com.google.cloud.tools.jib") version Versions.jib
}
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
implementation("gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:${Versions.jib}")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50")
implementation(gradleApi())
implementation(localGroovy())
}
And finally Docker.kt:
import org.gradle.api.Plugin
import org.gradle.api.Project
open class JibConfigPlugin : Plugin<Project> {
override fun apply(target: Project) {
//configureJib()
TODO("not implemented")
}
}
//internal fun Project.configureJib() = this.extensions.getByType<JibExtension>().run {}
internal fun Project.configureJib() = project.configure<JibExtension>() {
TODO("not implemented")
}
My problem is that I can't find the JibExtension, so when I try to implement and configure the Jib it doesn't work but in the build.gradle.kts everything works.
My problem is that I can't find the JibExtension
Plugins or extensions can be applied in a variety of different ways. You can "react" to the plugin being applied by using the withPlugin method of PluginManager:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
// Configuration happens inside this Action block.
}
}
}
Using this method you can be certain that a plugin exists/has been applied without forcing a user/project to use the plugin.
The Jib plugin offers a single extension and a variety of tasks.
Configuring the extension can be done with the following:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
project.extensions.configure<JibExtension> {
// Example configuring the `container`
container {
creationTime = "USE_CURRENT_TIMESTAMP"
}
}
}
}
}
Looking over the source of the Gradle plugin for Jib, the authors used lazy configuration for the tasks, so it is best to also use the same method to configure those tasks.
For example, to configure the jib task:
class JibConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.google.cloud.tools.jib") {
project.tasks.named<BuildImageTask>("jib") {
to {
setTargetImage("my_acr_name.azurecr.io/my-app")
}
}
}
}
}
The above uses the named method which returns a TaskProvider
Then simply apply your plugin as documented here: https://guides.gradle.org/writing-gradle-plugins/#apply_the_plugin_to_the_host_project
Source of the build.gradle.kts I used to test:
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
dependencies {
implementation("gradle.plugin.com.google.cloud.tools:jib-gradle-plugin:1.7.0")
}

dependencies added in subprojects.forEach in gradle multi-module kotlin DSL is not visible to sub projects

I have multi-module gradle project with kotlin dsl called stream-data-processing. It is in github here.
The build.gradle.kts file of root project is -
plugins {
base
java
}
allprojects {
group = "streams-data-processing"
version = "1.0"
repositories {
jcenter()
mavenCentral()
mavenLocal()
}
dependencies {
subprojects.forEach {
compile("org.apache.kafka:kafka-streams:2.2.0")
testImplementation("junit:junit:4.12")
}
}
}
settings.gradle.kts is -
rootProject.name = "stream-data-processing"
include ("word-count-demo")
I have some sub-project called word-count-demo.
The build.gradle.kts file for this sub project is -
plugins {
java
application
}
But the classes in kafka-streams are not available in word-count-demo.
when I did `gradle word-count-demo:dependencies, it doesn't show the kafka dependencies available to the sub project.
I don't want to explicitly specify the dependencies in every project.
What is the mistake that went wrong here?
It appears this would be adding the same dependencies multiple times. I think you need to flip it around and call dependencies inside subprojects, and outside of allprojects, like so:
allprojects {
group ...
version ...
repositories ...
}
subprojects {
dependencies {
compile("org.apache.kafka:kafka-streams:2.2.0")
testImplementation("junit:junit:4.12")
}
}

Import Cucumber with Gradle

I want to import cucumber.api.java.en.* into my groovy files, but cucmber.api will not be recognized as in my classpath. Thus every #Given or #When annotation is not recognized.
When I build with ./gradlew cucumber the .feature file is found and missing snippets are shown in the console. What do I have to include in my build.gradle to add above import into my classpath?
My gradle version is 2.2 and the cucumber related parts of my build.gradle file look like this:
dependencies {
testCompile 'info.cukes:cucumber-java:1.2.4'
testCompile 'info.cukes:cucumber-junit:1.2.4'
}
test {
testLogging.showStandardStreams = true
systemProperties System.getProperties()
}
configurations {
cucumberRuntime {
extendsFrom testRuntime
}
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "cucumber.api.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['-f', 'pretty', '--glue', 'gradle.cucumber', 'src/test/resources']
}
}
}
What am I missing?
You include info.cukes:cucumber-java:1.2.4 which is the jar containing the annotations you are missing. They are expected to be available in your test classpath.
To me, it sounds as an issue with your IDE.
If you are using IntelliJ IDEA, try to re-import the project. Click on the two rotating arrows in your Gradle tab and refresh the project.

integrationTestCompile gradle dependency ignored

Following a few blog posts I tried to create a separate source folder for integration testing in gradle. I wanted also to add some additinal (arquillian) dependencies to my integrationTest task, but the integrationTestCompile seems to be ignored and I get a compilation error with the additional dependecy not resolved. When I change the dependecy to testCompile it works fine. Why is that so and how to change it? My simple test class:
//compilation fails with [Static type checking] - The variable [ArquillianSputnik] is undeclared
#TypeChecked
#RunWith(ArquillianSputnik)
class TestSpec extends Specification {
}
and gradle.build:
apply plugin: 'groovy'
apply plugin: 'war'
war.dependsOn 'native2ascii'
task native2ascii << {
ant.delete() {
fileset(dir: "${processResources.destinationDir}") {
include(name: '*.properties')
}
}
ant.native2ascii(src: 'src/main/resources/',
dest: "${processResources.destinationDir}",
includes: '**/*.properties',
encoding: 'UTF-8')
}
repositories {
mavenCentral()
maven {
url 'http://repository.jboss.org/nexus/content/groups/public'
}
mavenLocal()
}
sourceSets.main.java.srcDirs = []
sourceSets.main.groovy.srcDirs += ["src/main/java"]
sourceSets {
integrationTest {
groovy.srcDir file('src/integration-test/groovy')
resources.srcDir file('src/integration-test/resources')
compileClasspath = sourceSets.main.output + configurations.testCompile
runtimeClasspath = output + compileClasspath + configurations.testRuntime
}
}
dependencies {
//(...) non-test dependencies cut out for clarity
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
testCompile 'cglib:cglib-nodep:2.2.2'
testCompile 'org.objenesis:objenesis:1.2'
//when integrationTestCompile is changed to testCompile the compilation works and the test is executed
integrationTestCompile 'org.jboss.arquillian.spock:arquillian-spock-container:1.0.0.Beta3'
integrationTestCompile 'org.jboss.arquillian.graphene:graphene-webdriver:2.0.3.Final'
integrationTestCompile 'org.jboss.as:jboss-as-arquillian-container-managed:7.2.0.Final'
}
task integrationTest(type: Test, dependsOn: 'test') {
testClassesDir = sourceSets.integrationTest.output.classesDir
classpath = sourceSets.integrationTest.runtimeClasspath
}
check.dependsOn 'integrationTest'
First, you want to add to the compile and runtime classpaths rather than replace them. This really just means using the += operator rather than the = one. Additionally, you really only want to add the other sourcesets output, we'll deal with configurations separately.
compileClasspath += sourcesets.main.output + sourcesets.test.output
runtimeClasspath += sourcesets.main.output + sourcesets.test.output
Next, we'll want to configure our integration test configurations. Usually, this just means making them extend the test and compile ones so that they contain all those dependencies as well.
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}

Resources