Adding integration tests to Kotlin project using the Kotlin Gradle DSL - java

I would like to add an additional "source set" to a Kotlin project that will contain integration tests. I have seen a few posts that talk about doing it for either a vanilla Java project or for Kotlin but using Groovy rather than the Kotlin Gradle DSL.
In summary, using the Kotlin Gradle DSL:
how to add an additional "source set" that can contain Kotlin code, Java code & resources for the purpose of separating integration tests from regular unit tests?
how to add an additional task and configuration to run the integration tests separately from unit tests?
I would expect the directory structure to look something like:
src
main
java
kotlin
resources
test
java
kotlin
resources
integration
java
kotlin
resources
Related:
https://ryanharrison.co.uk/2018/07/25/kotlin-add-integration-test-module.html
https://www.petrikainulainen.net/programming/gradle/getting-started-with-gradle-integration-testing/
How do I add a new sourceset to Gradle?
Thanks

First, create source set and configuration:
sourceSets {
create("intTest") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
}
val intTestImplementation: Configuration by configurations.getting {
extendsFrom(configurations.implementation.get())
}
val intTestRuntimeOnly: Configuration by configurations.getting {
extendsFrom(configurations.runtimeOnly.get())
}
And then, create the task to run them:
val integrationTest = task<Test>("integrationTest") {
description = "Runs integration tests"
group = "verification"
testClassesDirs = sourceSets["intTest"].output.classesDirs
classpath = sourceSets["intTest"].runtimeClasspath
shouldRunAfter("test")
}
Also, you can add dependencies to be used by the new source set. For instance:
intTestImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
intTestRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitVersion")

You must add following configuration to your build.gradle file
configurations {
integrationTestImplementation.extendsFrom implementation
integrationTestRuntimeOnly.extendsFrom runtimeOnly
}
dependencies {
intTestImplementation 'junit:junit:4.12'
...
}
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integrationTest/kotlin')
}
}
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
useJUnitPlatform()
}
source:
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests

Related

why do I have to include the output of main in a new configuration if the classes in the configuration are in the same package?

Let's assume a java project with a project structure like so:
src
itest
java
SourcesTestsItest.java
main
java
gradle_pr
pojo.java
test
java
gradle_pr
SourceSetsTest.java
build.gradle
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.3/userguide/java_library_plugin.html
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
sourceSets{
itest{
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
java{
srcDirs("src/itest")
}
}
}
configurations{
itestImplementation.extendsFrom(testImplementation)
itestRuntimeOnly.extendsFrom(testRruntimeOnly)
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:28.2-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
implementation('org.apache.httpcomponents:httpclient:4.5.12')
itestImplementation('com.google.guava:guava:29.0-jre')
}
task printSourceSetInformation(){
doLast{
sourceSets.each { srcSet ->
println "["+srcSet.name+"]"
print "-->Source directories: "+srcSet.allJava.srcDirs+"\n"
print "-->Output directories: "+srcSet.output.classesDirs.files+"\n"
print "-->Compile classpath:\n"
srcSet.compileClasspath.files.each {
print " "+it.path+"\n"
}
println ""
}
}
}
task itest(type: Test) {
description = "Run integration tests"
group = "verification"
testClassesDirs = sourceSets.itest.output.classesDirs
classpath = sourceSets.itest.runtimeClasspath
}
Why do I have use the following lines
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
if the pojo class should be visible in test class under itest as they are in the same package?. How does the default configuration work so test cases can see the generated compiled code of main?
Gradle doesn't know per se how your configurations depend on each other (i.e. it could be that the main configuration depends on itest or the other way around).
For the main and test configurations this dependency is established for you when you apply the java-library plugin.

IntelliJ creating Integration Testing Environment

I'm creating another source set for integration testing, but every time my gradle rebuilds it turns my main test root folder back to source root folder (blue), when I want it to be test root folder (green). What do I need to configure in my build.gradle so that this is fixed?
sourceSets {
integration {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
intTestRuntimeOnly.extendsFrom runtimeOnly
intTestImplementation.extendsFrom testImplementation
}
dependencies {
intTestImplementation 'junit:junit:4.12'
}
task integrationTest(type: Test) {
description = 'Runs integration tests.'
group = 'verification'
testClassesDirs = sourceSets.integration.output.classesDirs
classpath = sourceSets.integration.runtimeClasspath
shouldRunAfter test
}
check.dependsOn integrationTest
Everytime I manually change it to testsources root and rebuild it just turns back to blue.
I followed this documentation https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
Thanks

Can IntelliJ import non-standard source directories from Gradle?

I am converting over to using IntelliJ (version 2019.1). The multi-project directory structure used has the standard src/main/java and src/test/java for each project, but additionally has some non-standard ones such as: src/testsupport/java.
Gradlew (using the internal/recommended gradlew packaged within IntelliJ) is used to import the projects. The Gradle build files include both:
apply plugin: 'idea'
apply plugin: 'java'
Edited to improve clarity
Every project imports fine. Interproject references work to the standard directories. However, when I am in Project B, but need access to src/generated/java or src/testsupport/java from Project A, those are not imported (import statements that compile fine from the gradle command line show up as unresolvable within IntelliJ). Is there a configuration change or something needed to make these take effect?
Currently, I have:
subprojects {
idea {
module {
testSourceDirs += project.sourceSets.generated.java.srcDirs
testSourceDirs += project.sourceSets.testsupport.java.srcDirs
}
}
}
You need help Gradle out by creating a source set for the custom sources your projects define. So from your question, something like:
(using Kotlin DSL)
allprojects {
apply {
plugin("idea")
plugin("java-library")
}
repositories {
mavenCentral()
}
configure<SourceSetContainer> {
create("generated") {
compileClasspath += project.the<SourceSetContainer>()["main"].output
runtimeClasspath += project.the<SourceSetContainer>()["main"].output
}
create("testsupport") {
compileClasspath += project.the<SourceSetContainer>()["main"].output
runtimeClasspath += project.the<SourceSetContainer>()["main"].output
}
}
val api by configurations
val testImplementation by configurations
val testRuntimeOnly by configurations
dependencies {
api(platform("org.junit:junit-bom:5.5.1"))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
}
val test by tasks.getting(Test::class) {
useJUnitPlatform()
}
}
The above will give you:
So now you want to use projectA in projectB, so projectB's Gradle file would include a dependency on projectA:
dependencies {
implementation(":projectA")
}
This should hopefully get you started. Keep in mind, the examples given above use the Kotlin DSL which you should be able to convert back to Groovy.
References:
https://docs.gradle.org/current/userguide/java_plugin.html#source_sets
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests

Running Cucumber from different directory using Gradle 5 and Cucumber-JVM

I'm fairly new to Gradle and am using version 5.0. I'm reasonably proficient in Cucumber.
I've got a simple project that builds a jar file and runs JUnit tests on them. It all works well.
Now I want to add Cucumber to the project but I want my .feature files and the related stepdefs in an alternative source-tree (sourceSet in Gradle terminology).
The sources can be found on Github, which holds a sample project.
My source tree should look like this:
src/
cucumberTest/
java/
/...
resources/
/...
main/
java/
/...
resources/
/...
test/
/java
/...
resources/
/...
When I put the .feature files in cucumberTest/resources and the stepdef java files in test/java my Cucumber tests run fine. But when the stepdefs are in cucumberTest/java Cucumber can't find the files and I get the error that they are not defined.
Undefined scenarios:
src/cucumberTest/resources/is_it_saturday_yet.feature:4 # Sunday isn't Saturday
2 Scenarios (1 undefined, 1 passed)
6 Steps (1 skipped, 2 undefined, 3 passed)
0m0.134s
My build.gradle file is this:
plugins {
id 'java-library'
id 'java'
id 'idea'
}
repositories {
jcenter()
mavenCentral()
}
archivesBaseName = "helloworld"
version = '1.0'
dependencies {
api 'org.apache.commons:commons-math3:3.6.1'
implementation 'com.google.guava:guava:26.0-jre'
testImplementation 'junit:junit:4.12'
testCompile("junit:junit:4.12")
testCompile('org.junit.jupiter:junit-jupiter-api:5.3.2')
testCompile('org.junit.jupiter:junit-jupiter-params:5.3.2')
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
testRuntime("org.junit.vintage:junit-vintage-engine:5.3.2")
testCompile 'io.cucumber:cucumber-java:4.2.0'
testCompile 'io.cucumber:cucumber-junit:4.2.0'
}
configurations {
cucumberRuntime {
extendsFrom testRuntime
}
}
test {
useJUnitPlatform ()
testLogging {
events "passed", "skipped", "failed"
}
}
// Cucumber stuff:
sourceSets {
cucumberTest {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
cucumberTestImplementation.extendsFrom implementation
cucumberTestRuntimeOnly.extendsFrom runtimeOnly
}
dependencies {
testCompile 'io.cucumber:cucumber-java:4.2.0'
testCompile 'io.cucumber:cucumber-junit:4.2.0'
}
task cucumberTest() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "cucumber.api.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + sourceSets.cucumberTest.output
args = ['--plugin', 'pretty', '--glue', 'stepdefs.hellocucumber', 'src/cucumberTest/resources']
}
}
}
wrapper {
gradleVersion = '5.0'
}
I've looked all over the web, but I feel like I'm the only one who wants to use cucumber-jvm with Gradle 5 and have the BDD tests (cucumber) separated from the TDD tests (JUnit).
I can take the easy route and just mix them both, but apart from being a little puritan and have BDD and TDD separated, I also want to understand what's going on and why it's not working.
Help is appreciated. Thanks.
Iwan
With the help of Bart Kors, a good friend of mine, I was able to get it to work as intended. I updated the Github repository to include the working code. Do a clone of TrheeAxis/hellocucumber to get the working code.

Gradle equivalent of Surefire classpathDependencyExclude

I'm trying to migrate java project from maven to gradle. The problem is very tricky classpath dependency configuration for tests now.
Our maven-surefire-plugin configuration:
<includes>
<include>**/SomeTest1.java</include>
</includes>
<classpathDependencyExcludes>
<classpathDependencyExclude>com.sun.jersey:jersey-core</classpathDependencyExclude>
</classpathDependencyExcludes>
There are different classpathes for different test-classes. How can I implement it with Gradle?
First of all you need to differ your tests sources into separate sourceSets. Say, we need to run tests from package org.foo.test.rest with a little different runtime classpath, than other tests. Thus, its execution will go to otherTest, where remain tests are in test:
sourceSets {
otherTest {
java {
srcDir 'src/test/java'
include 'org/foo/test/rest/**'
}
resources {
srcDir 'src/test/java'
}
}
test {
java {
srcDir 'src/test/java'
exclude 'org/foo/rest/test/**'
}
resources {
srcDir 'src/test/java'
}
}
}
After that, you should make sure that otherTest has all required compile and runtime classpaths set correctly:
otherTestCompile sourceSets.main.output
otherTestCompile configurations.testCompile
otherTestCompile sourceSets.test.output
otherTestRuntime configurations.testRuntime + configurations.testCompile
The last thing is to exclude (or include) unneeded runtime bundles from test:
configurations {
testRuntime {
exclude group: 'org.conflicting.library'
}
}
And to create Test Gradle task for otherTest:
task otherTest(type: Test) {
testClassesDir = sourceSets.otherTest.output.classesDir
classpath += sourceSets.otherTest.runtimeClasspath
}
check.dependsOn otherTest
Use next workaround:
Create source set for needed tests
Add configurations for created sourceSet
Add task for run test with custom configuration
Configure test task dependOn customized test task
Configure Report plugin for generate beautiful html report :)
Like this getting started

Categories