Part 5 · 1 chapters · ~8 min
Tooling and Testing
Gradle with the Kotlin DSL and version catalogs, IntelliJ, JUnit 5 and Kotest, MockK, testing coroutines with runTest and virtual time, Testcontainers for Postgres, property-based tests, ktlint, detekt and Kover, and CI.
6
Deterministic tests for async code
code
@Test fun `retries a timed-out rail call three times then marks unknown`() = runTest {
val rail = mockk<Rail>()
coEvery { rail.send(any()) } throws TimeoutException()
val result = PayoutService(rail, repo).submit(payout) // contains delay(2_000 * 2^n) backoff
assertEquals(PayoutState.UNKNOWN, result.state) // runs instantly on virtual time
coVerify(exactly = 3) { rail.send(any()) }
}
class LedgerProps : StringSpec({
"postings always sum to zero" { checkAll(Arb.long(1L..10_000_000L), Arb.long(0L..5_000L)) { amount, fee ->
postingsFor(Kobo(amount), Kobo(fee)).sumOf { it.amount.value } shouldBe 0L } }
})
// Testcontainers: a real Postgres per test class
@Container val pg = PostgreSQLContainer("postgres:16")TOOLING AND TESTING
the Kotlin toolbox
swipe the figure sideways, or tap expand for full screen
1/4
builds
Gradle with the Kotlin DSL is the standard build; Maven works too. Version catalogs (libs.versions.toml) keep dependencies consistent across modules.
Gradle Kotlin DSLversion catalogs