Tests with Kotlin


Introduction

Tests are important for software development to ensure qualitz and maintainability.

Tests in Kotlin can be done in a very similiar way as in Java. The basic concepts like unittests, integrationtests, mocking, dependency injection, etc can also be applied to Kotlin code.

I will use the framework JUnit 5 throughout this tutorial. It is the Java test framework and fully compatible with Kotlin.

The first test

Kotlin can use the same Annotations Java uses so our first test looks pretty similiar:

class MyTest {
    
    @Test
    fun firstTest(){
        assert(2==2)
    }
}

We can also use the annotations @Before, @BeforeEach, @After, @AfterEach to create setup and teardown methods.

Backtick testcase

Kotlin allows to define test methods with the backtick notation instead of method names. This allows a better readable test description.

class MyTest {
    @Test
    fun `this is a simple backtick testcase`() {
        assert(listOf(1, 2, 3).size == 3)
    }
}

If this notation looks familiar to you, its propably because some other languages also have this notation. I experienced it in Scala.

Nested Testcases

JUnit 5 introduced the @Nested annotations. This allows nested testcases with inner classes:

class MyTest {
    @Nested
    inner class TestNested {

        @Test
        fun `this is a nested testcase`() {
          assert(true != false)
        }
    }
}

This allows the developer create groups for test cases and therefore improve structure and readability.

This feature is not special for Kotlin. You can also use it in Java to structure tests.

Kotlin test frameworks

In this section I want to introduce some test frameworks that are special for Kotlin.

They cover the use cases of better assertions and mocking.

Fluent assertions with assertk

Assertk is inspired by the Java assertion library AssertJ.

It allows to create assertions that are easy to read.

import me.tatarka.assertk.assert
import me.tatarka.assertk.assertions.*
import org.junit.jupiter.api.Test

class MyTest {

    @Test
    fun firstTest(){
        // simple string assertion
        assert("Hello World").endsWith("World")

        // list assertion
        assert(listOf(1, 2, 3)).hasSize(3)

        // assertion of exception
        assert {
            throw Exception("Test")
        }.throwsError {
            it.hasMessage("Test")
        }
    }
}

It’s currently available in version 1.0.SNAPSHOT and maybe we can expect a 1.0 release candidate soon.

One drawback in my eyes is that, you need to manually import the assert method because Kotlin already has a assert function. This makes it a bit unintuitive.

Mocking with Mockk

Mockk allows for a more functional mocking style compared to Mockito.

Suspect we have an interface defined as follows:

interface TestInterface {
    fun name(): String
    fun number(): Int
    fun exception(): String
}

Than we can mock it with the mockk function and define actions using every:

  @Test
  fun mockedCase() {
      // mock interface
      val mockedInterface = mockk<TestInterface>()

      // define actions with every function
      every { mockedInterface.name() }.returns("test")
      every { mockedInterface.number() }.returnsMany(listOf(1, 2, 3))
      every { mockedInterface.exception() }.throws(Exception("test"))

      // assert the created mock
      assert(mockedInterface.name()).equals("test")

      assert(mockedInterface.number()).equals(1)
      assert(mockedInterface.number()).equals(2)
      assert(mockedInterface.number()).equals(3)

      assert { mockedInterface.exception() }.throwsError { it.hasMessage("test") }
  }

Wrapping up

Tests in Kotlin can be realized in a similiar way to Java. We can use general concepts like unittests, mocking, assertions, etc.

With the backtick and nested notation are we able to define a clear test structure and description.

Frameworks like Assertk and Mockk help to write tests in a functional way.