Running Kotlin Tests With Gradle

After we have finished this webinar, we can create a Gradle project that can compile and run both unit and integration tests which use Kotlin and JUnit 5.

Get the source code from Github .

The Requirements of Our Gradle Build

The requirements of our Gradle build are:

  • All code (application and tests) must use Kotlin.
  • Unit and integration tests must have separate source and resource directories.
  • It must be possible to run only unit or integration tests.
  • It must be possible to run both unit and integration tests.
  • If an integration test fails, our build must fail as well.
  • Integration and unit tests must have different HTML report directories.

Let’s start by taking a quick look at the system under test.

Introduction to System Under Test

During this webinar we will write both unit and integration tests for the MessageService class. This class has one method called getMessage() which returns the message: ‘Hello World!’.

The source code of the MessageService class looks as follows:

1class MessageService {
2
3    fun getMessage(): String {
4        return "Hello World!"
5    }
6}

Next, we will find out how we can create a simple Kotlin project that can run only unit tests.

Running Unit Tests With Gradle

When we want to create a Kotlin project that can run only unit tests, we have to follow these steps:

First, because we are using JVM, we have to apply the Kotlin JVM Gradle plugin. After we have applied this plugin, our build.gradle file looks as follows:

1plugins {
2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
3}

Second, we have ensure that Gradle uses the Maven central repository when it resolves the dependencies of our Kotlin project. After we have configured the used repository, the source code of our build.gradle file looks as follows:

1plugins {
2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
3}
4
5repositories {
6    mavenCentral()
7}

Third, we have to specify the dependencies of our application. Because our application uses Kotlin, we have to add the The Kotlin standard library dependency to the implementation dependency configuration.

After we have done this, our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3}
 4
 5repositories {
 6    mavenCentral()
 7}
 8
 9dependencies {
10    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
11}

Fourth, we have to specify the testing dependencies. Because we want to write tests for a Kotlin application by using Kotlin and JUnit 5, we can specify the testing dependencies by following these steps:

  1. Add the AssertJ Core (not mandatory) dependency to the testCompile dependency configuration.
  2. Add the JUnit Jupiter API dependency to the testCompile dependency configuration.
  3. Add the JUnit Jupiter Engine dependency to the testRuntime dependency configuration.

After we have specified the testing dependencies, the source code of our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3}
 4
 5repositories {
 6    mavenCentral()
 7}
 8
 9dependencies {
10    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
11    testCompile(
12            'org.assertj:assertj-core:3.11.1',
13            'org.junit.jupiter:junit-jupiter-api:5.3.2'
14    )
15    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
16}

Fifth, we have to ensure that Gradle runs our unit tests by using JUnit 5. After we have configured the test task, our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3}
 4
 5repositories {
 6    mavenCentral()
 7}
 8
 9dependencies {
10    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
11    testCompile(
12            'org.assertj:assertj-core:3.11.1',
13            'org.junit.jupiter:junit-jupiter-api:5.3.2'
14    )
15    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
16}
17
18test {
19    useJUnitPlatform()
20}

We have now configured our Gradle build. However, before we can run our unit tests, we have to write one test class and put this class to the src/test/kotlin directory. The source code of our test class looks as follows:

 1import org.assertj.core.api.Assertions.assertThat
 2import org.junit.jupiter.api.BeforeEach
 3import org.junit.jupiter.api.DisplayName
 4import org.junit.jupiter.api.Test
 5
 6class MessageServiceTest {
 7
 8    private lateinit var service: MessageService
 9
10    @BeforeEach
11    fun configureSystemUnderTest() {
12        service = MessageService()
13    }
14
15    @Test
16    @DisplayName("Should return the correct message")
17    fun shouldReturnCorrectMessage() {
18        val message = service.getMessage()
19        assertThat(message).isEqualTo("Hello World!")
20    }
21}

We can now run our unit tests by using the command: gradle clean test. When we run this command, we see that Gradle compiles our application, and compiles and runs our unit tests.

 1> Task :clean
 2> Task :compileKotlin
 3> Task :compileJava NO-SOURCE
 4> Task :processResources NO-SOURCE
 5> Task :classes UP-TO-DATE
 6> Task :compileTestKotlin
 7> Task :compileTestJava NO-SOURCE
 8> Task :processTestResources NO-SOURCE
 9> Task :testClasses UP-TO-DATE
10> Task :test

If you are using Gradle 5 and you want to see the full list of invoked tasks, you should run Gradle by using the info command line option.

Additional Reading:

So, we have just created a Gradle project that can compile and run our unit tests. However, our build doesn’t have any support for integration tests. Let’s move on and find out how we can support both unit and integration tests.

Running Unit and Integration Tests With Gradle

Because we want to run both unit and integration tests with Gradle, we have to make some changes to our Gradle build. We can make these changes by following these steps:

First, because we want to add new test and resource directories to our Gradle build, we have to apply the Gradle TestSets plugin . After we have applied this plugin, our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19test {
20    useJUnitPlatform()
21}

Second, we have to configure the source and resource directories of our integration tests. We can configure these directories by following these steps:

  1. Add a new test set called integrationTest to our Gradle build.
  2. Change the name of the test set’s root directory from integrationTest to integration-test.

After we have configured these directories, the source code of the build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19testSets {
20    integrationTest { dirName = 'integration-test' }
21}
22
23test {
24    useJUnitPlatform()
25}
This configuration adds a source set called integrationTest to our build. This means that the src/integration-test/kotlin directory contains the source code of our our integration tests and the src/integration-test/resources directory contains the resources of our integration tests.

Third, we have to ensure that unit tests are run before integration tests. After we added the required configuration to our build.gradle file, it looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19testSets {
20    integrationTest { dirName = 'integration-test' }
21}
22
23integrationTest.mustRunAfter test
24
25test {
26    useJUnitPlatform()
27}

Fourth, we have to ensure that integration tests are run when we invoke the build task. Also, we must ensure that our build fails if an integration test fails. After we added the required configuration to our build.gradle file, it looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19testSets {
20    integrationTest { dirName = 'integration-test' }
21}
22
23check.dependsOn integrationTest
24integrationTest.mustRunAfter test
25
26test {
27    useJUnitPlatform()
28}

Fifth, we have to ensure that Gradle runs our integration tests by using JUnit 5. After we have configured the integrationTest task, our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19testSets {
20    integrationTest { dirName = 'integration-test' }
21}
22
23check.dependsOn integrationTest
24integrationTest.mustRunAfter test
25
26integrationTest {
27    useJUnitPlatform()
28}
29
30test {
31    useJUnitPlatform()
32}

Sixth, we have to filter the invoked tests by using JUnit 5 tags. We can do this by following these steps:

  1. Ensure that JUnit 5 runs only the tests which have the tag: unitTest when we run our unit tests.
  2. Ensure that JUnit 5 runs only the tests which have the tag: integrationTest when we run our integration tests.

After we have configured the invoked tests, the source code of our build.gradle file looks as follows:

 1plugins {
 2    id 'org.jetbrains.kotlin.jvm' version '1.3.20'
 3    id 'org.unbroken-dome.test-sets' version '2.1.1'
 4}
 5
 6repositories {
 7    mavenCentral()
 8}
 9
10dependencies {
11    implementation('org.jetbrains.kotlin:kotlin-stdlib:1.3.20')
12    testCompile(
13            'org.assertj:assertj-core:3.11.1',
14            'org.junit.jupiter:junit-jupiter-api:5.3.2'
15    )
16    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
17}
18
19testSets {
20    integrationTest { dirName = 'integration-test' }
21}
22
23check.dependsOn integrationTest
24integrationTest.mustRunAfter test
25
26integrationTest {
27    useJUnitPlatform {
28        includeTags 'integrationTest'
29    }
30}
31
32test {
33    useJUnitPlatform {
34        includeTags 'unitTest'
35    }
36}

We have now created a Gradle build that compile and run our unit and integration tests. Next, we will find out how we can write unit and integration tests which are run by Gradle.

Writing Unit and Integration Tests

We can write our unit and integration tests by following these steps:

First, because we already wrote our unit test class, the only thing that we have to do is to add the tag: unitTest to our unit test class.

After we have added this tag to our unit test class, its source code looks as follows:

 1import org.assertj.core.api.Assertions.assertThat
 2import org.junit.jupiter.api.BeforeEach
 3import org.junit.jupiter.api.DisplayName
 4import org.junit.jupiter.api.Tag
 5import org.junit.jupiter.api.Test
 6
 7@Tag("unitTest")
 8class MessageServiceTest {
 9
10    private lateinit var service: MessageService
11
12    @BeforeEach
13    fun configureSystemUnderTest() {
14        service = MessageService()
15    }
16
17    @Test
18    @DisplayName("Should return the correct message")
19    fun shouldReturnCorrectMessage() {
20        val message = service.getMessage()
21        assertThat(message).isEqualTo("Hello World!")
22    }
23}

Second, we have to write an integration test class which ensures that the system under test returns the correct message. Also, we must add the tag: integrationTest to our integration test class.

After we have written our integration test class, its source code looks as follows:

 1import org.assertj.core.api.Assertions
 2import org.junit.jupiter.api.BeforeEach
 3import org.junit.jupiter.api.DisplayName
 4import org.junit.jupiter.api.Tag
 5import org.junit.jupiter.api.Test
 6
 7@Tag("integrationTest")
 8class GetMessageTest {
 9
10    private lateinit var service: MessageService
11
12    @BeforeEach
13    fun configureSystemUnderTest() {
14        service = MessageService()
15    }
16
17    @Test
18    @DisplayName("Should return the correct message")
19    fun shouldReturnCorrectMessage() {
20        val message = service.getMessage()
21        Assertions.assertThat(message).isEqualTo("Hello World!")
22    }
23}

We have now written our unit and integration tests. Let’s move on and find out how we can run our tests.

Running Our Tests

If we want to run only unit tests, we have to run the command: gradle clean test. When we run this command at command prompt, we see that Gradle runs only our unit tests:

 1> Task :clean
 2> Task :compileKotlin
 3> Task :compileJava NO-SOURCE
 4> Task :processResources NO-SOURCE
 5> Task :classes UP-TO-DATE
 6> Task :compileTestKotlin
 7> Task :compileTestJava NO-SOURCE
 8> Task :processTestResources NO-SOURCE
 9> Task :testClasses UP-TO-DATE
10> Task :test

If we want to run only integration tests, we have to run the command: gradle clean integrationTest. When we run this command at command prompt, we see that Gradle runs only our integration tests:

 1> Task :clean
 2> Task :compileKotlin
 3> Task :compileJava NO-SOURCE
 4> Task :processResources NO-SOURCE
 5> Task :classes UP-TO-DATE
 6> Task :compileIntegrationTestKotlin
 7> Task :compileIntegrationTestJava NO-SOURCE
 8> Task :processIntegrationTestResources NO-SOURCE
 9> Task :integrationTestClasses UP-TO-DATE
10> Task :integrationTest

If we want to run both unit and integration tests, we have to run one of these two commands: gradle clean test integrationTest or gradle clean build. When we run the build task, we see that Gradle runs both unit and integration tests:

 1> Task :clean
 2> Task :compileKotlin
 3> Task :compileJava NO-SOURCE
 4> Task :processResources NO-SOURCE
 5> Task :classes UP-TO-DATE
 6> Task :inspectClassesForKotlinIC
 7> Task :jar
 8> Task :assemble
 9> Task :compileTestKotlin
10> Task :compileTestJava NO-SOURCE
11> Task :processTestResources NO-SOURCE
12> Task :testClasses UP-TO-DATE
13> Task :test
14> Task :compileIntegrationTestKotlin
15> Task :compileIntegrationTestJava NO-SOURCE
16> Task :processIntegrationTestResources NO-SOURCE
17> Task :integrationTestClasses UP-TO-DATE
18> Task :integrationTest
19> Task :check
20> Task :build

We can now create a Gradle build that can run unit and integration tests which use Kotlin and JUnit 5. Let’s summarize what we learned from this webinar.

Summary

This webinar has taught us five things:

  • Because we are using JVM, we have to apply the Kotlin JVM Gradle plugin.
  • We have to add the Kotlin standard library (kotlin-stdlib) dependency to the implementation dependency configuration.
  • We can add new test and resource directories to our Gradle build by using the Gradle TestSets plugin.
  • If we want to run tests which use JUnit 5, we have to enable the Gradle’s built-in JUnit 5 support.
  • We can filter the invoked tests by using JUnit 5 tags.

Get the source code from Github .