July 25, 2020
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.