kotlinintermediate
Testing with JUnit 5 and Kotlin
Write expressive tests in Kotlin: JUnit 5, nested tests, parameterized tests, and extension functions.
kotlinPress ⌘/Ctrl + Shift + C to copy
import org.junit.jupiter.api.*
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.junit.jupiter.params.provider.MethodSource
import java.util.stream.Stream
// Class under test
class Calculator {
fun add(a: Int, b: Int) = a + b
fun divide(a: Int, b: Int): Double {
require(b != 0) { "Cannot divide by zero" }
return a.toDouble() / b
}
fun isEven(n: Int) = n % 2 == 0
}
class CalculatorTest {
private lateinit var calc: Calculator
@BeforeEach
fun setup() {
calc = Calculator()
}
@Test
fun `add should sum two numbers`() {
assertEquals(4, calc.add(2, 2))
assertEquals(0, calc.add(-1, 1))
assertEquals(-3, calc.add(-1, -2))
}
@Test
fun `divide should return correct result`() {
assertEquals(2.5, calc.divide(5, 2))
}
@Test
fun `divide by zero should throw`() {
val exception = assertThrows<IllegalArgumentException> {
calc.divide(1, 0)
}
assertTrue(exception.message!!.contains("zero"))
}
@Nested
@DisplayName("Parameterized Tests")
inner class Parameterized {
@ParameterizedTest(name = "{0} + {1} = {2}")
@CsvSource(
"1, 1, 2",
"2, 3, 5",
"10, -5, 5",
"0, 0, 0"
)
fun `add with parameters`(a: Int, b: Int, expected: Int) {
assertEquals(expected, calc.add(a, b))
}
@ParameterizedTest
@MethodSource("evenNumbers")
fun `isEven should return true for even numbers`(n: Int) {
assertTrue(calc.isEven(n))
}
companion object {
@JvmStatic
fun evenNumbers(): Stream<Int> = Stream.of(0, 2, 4, 100, -2)
}
}
@Nested
inner class EdgeCases {
@Test
fun `add with max int`() {
// Check overflow behavior
val result = calc.add(Int.MAX_VALUE, 1)
assertTrue(result < 0) // overflow
}
}
}
// Custom assertions via extension functions
fun <T> T.shouldEqual(expected: T) {
assertEquals(expected, this)
}
fun <T> T.shouldNotBeNull(): T {
assertNotNull(this)
return this
}
fun (() -> Unit).shouldThrow<T : Throwable>(): T {
return assertThrows(T::class.java) { this() }
}
infix fun <T> T.shouldBe(expected: T) = assertEquals(expected, this)
// Usage of custom assertions
class CustomAssertionTest {
@Test
fun `custom assertions demo`() {
val calc = Calculator()
calc.add(2, 3) shouldBe 5
calc.divide(10, 4).shouldEqual(2.5)
"hello".shouldNotBeNull()
}
}Use Cases
- Unit testing Kotlin classes and functions
- Parameterized test suites
- Custom assertion DSLs for readable tests
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinintermediate
Testing with Kotest and Assertions
Write expressive tests with Kotest: string spec, data-driven tests, property-based testing, and matchers.
Best for: Expressive Kotlin unit testing
#kotlin#testing
kotlinintermediate
Testing with MockK Framework
Mock dependencies in Kotlin tests with MockK: relaxed mocks, verify, coEvery, and slot captures.
Best for: Unit testing with mocked dependencies
#kotlin#testing
kotlinbeginner
Null Safety — Elvis, Safe Call, and let
Master Kotlin null safety: safe calls, Elvis operator, let/also scoping, and smart casts.
Best for: Safe navigation through nullable chains
#kotlin#null-safety
kotlinbeginner
Data Classes — Copy, Destructure, and Equals
Use data classes for immutable models: auto-generated equals, hashCode, copy, and destructuring.
Best for: Immutable domain models and DTOs
#kotlin#data-class