I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest)

Kotest makes testing fun!“I don’t always test my code, but when I do, I do it in prod.” You’ve probably seen this meme. Funny… until it isn’t.In the Kotlin world, testing is often treated like that dusty treadmill in the garage: you know you should use…


This content originally appeared on Level Up Coding - Medium and was authored by Rafa

Kotest makes testing fun!

“I don’t always test my code, but when I do, I do it in prod.”
You’ve probably seen this meme. Funny… until it isn’t.

In the Kotlin world, testing is often treated like that dusty treadmill in the garage: you know you should use it, but you’d rather just “ship it.” The problem? Bugs don’t care about your deadlines.

That’s where Kotest comes in. Not only does it make testing idiomatic to Kotlin, it also makes it… dare I say… fun. Even better, with Kotest you can eliminate the sprawl of multiple test libraries in your Gradle build and stick to just one.

Why Testing Matters (Yes, Even in Kotlin)

Kotlin is expressive, concise, and null-safe. But none of that prevents:

  • Logic errors (> instead of >=)
  • Incorrect assumptions (like “this list is never empty”)
  • Fragile refactors that silently break existing code

Tests give you confidence. Not just that the code works today, but that it’ll keep working tomorrow when you inevitably “just tweak one thing.”

Why Kotest?

Unlike JUnit (which often feels like Java’s hand-me-downs), Kotest is built for Kotlin developers.

Here’s what makes it shine:

  • Idiomatic syntax: Assertions read like English (a shouldBe b)
  • Spec styles: Choose from StringSpec, FunSpec, ShouldSpec, etc.—write tests in the style that fits your brain
  • Built-in matchers: Collections, exceptions, data classes, coroutines, JSON… no need to import five assertion libs
  • One library to rule them all: No mixing JUnit assertions, Truth, Hamcrest, and Mockk. Kotest covers your bases.

Getting Started (Gradle Setup)

With a version catalog (libs.versions.toml):

[versions]
kotest = "5.9.1"

[libraries]
kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }

In build.gradle.kts:

dependencies {
testImplementation(libs.kotest.assertions.core)
testImplementation(libs.kotest.runner.junit5)
}

And don’t forget the JUnit 5 engine:

tasks.withType<Test> {
useJUnitPlatform()
}

Example 1: Assertions That Read Like English

@Test
fun `isPalindrome returns true for a palindrome word`() {
isPalindrome("racecar") shouldBeEqual true
isPalindrome("hello") shouldBeEqual false
isPalindrome("RaceCar") shouldBeEqual true
isPalindrome("A man, a plan, a canal: Panama") shouldBeEqual true
}

fun isPalindrome(word: String): Boolean {
val normalizeWord = word.lowercase().filter { it.isLetterOrDigit() }
return normalizeWord == normalizeWord.reversed()
}

That’s it — no boilerplate, no static imports, no ugly assertEquals(expected, actual).

A Real Kotest Example in Kotlin

Let’s take a simple example from my compose Android ViewModel that manages a modal dialog.
I want to ensure that:

  1. The dialog starts closed.
  2. Calling openDialog() sets the state to opened.
  3. Calling closeDialog() sets the state back to closed.

Here’s the test written with Kotest:

@Test
fun `openDialog should open dialog, closeDialog should close dialog`() = runTest {
// Given
val mockTopAgitator = mockAgitators[0]
coEvery { mockTopAgitatorsRepository.getTopAgitator(mockTopAgitator.id) } returns mockTopAgitator
coEvery { mockTopAgitatorsRepository.getAgitatorsCount() } returns mockAgitators.size

// When
viewModel.getTopAgitator()
testDispatcher.scheduler.advanceUntilIdle()

// Then
val state1 = viewModel.uiState.value
(state1 is TopAgitatorsUiState.Success) shouldBe true
(state1 as TopAgitatorsUiState.Success).topAgitator shouldBe mockTopAgitator
state1.dialogModalState shouldBe AgitatorModalState.CLOSED

// When
viewModel.openDialog()
testDispatcher.scheduler.advanceUntilIdle()

// Then
val state2 = viewModel.uiState.value
(state2 is TopAgitatorsUiState.Success) shouldBe true
(state2 as TopAgitatorsUiState.Success).topAgitator shouldBe mockTopAgitator
state2.dialogModalState shouldBe AgitatorModalState.OPENED

// When
viewModel.closeDialog()
testDispatcher.scheduler.advanceUntilIdle()

// Then
val state3 = viewModel.uiState.value
(state3 is TopAgitatorsUiState.Success) shouldBe true
(state3 as TopAgitatorsUiState.Success).topAgitator shouldBe mockTopAgitator
state3.dialogModalState shouldBe AgitatorModalState.CLOSED
}

enum class AgitatorModalState { OPENED, CLOSED }

@HiltViewModel
class TopAgitatorsViewModel @Inject constructor(
val repository: TopAgitatorsRepository,
val dataStoreHelper: DataStoreHelper,
private val interstitialAdFactory: InterstitialAdFactory
) : ViewModel() {

private val _uiState = MutableStateFlow<TopAgitatorsUiState>(TopAgitatorsUiState.Loading)
val uiState: StateFlow<TopAgitatorsUiState> = _uiState.asStateFlow()

fun openDialog() {
val currentState = _uiState.value
if (currentState is TopAgitatorsUiState.Success) {
_uiState.value = currentState.copy(agitatorModalState = AgitatorModalState.OPENED)
}
}

fun closeDialog() {
val currentState = _uiState.value
if (currentState is TopAgitatorsUiState.Success) {
_uiState.value = currentState.copy(agitatorModalState = AgitatorModalState.CLOSED)
}
}
}
Voilà, the viewModel is 100% covered!

Wrapping Up

With Kotest, testing doesn’t feel like work. It feels like Kotlin.

✨ Concise syntax
✨ Fun, readable test specs
✨ One dependency instead of a messy soup of assertion libraries

I don’t always test my code…
But when I do, I do it in prod — or with Kotest. 😉

So maybe you don’t always test your code. But when you do, make it fun, idiomatic, and productive — with Kotest.

🔥 See it in action!
Check out my app Hockey Hub — (almost) fully tested with Kotest and boasting 90% code coverage: Download Hockey Hub on Google Play

🗣️: reach out on LinkedIn, X or Insta

Best,
Rafa


I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest) was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Rafa


Print Share Comment Cite Upload Translate Updates
APA

Rafa | Sciencx (2025-08-25T01:53:29+00:00) I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest). Retrieved from https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/

MLA
" » I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest)." Rafa | Sciencx - Monday August 25, 2025, https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/
HARVARD
Rafa | Sciencx Monday August 25, 2025 » I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest)., viewed ,<https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/>
VANCOUVER
Rafa | Sciencx - » I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/
CHICAGO
" » I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest)." Rafa | Sciencx - Accessed . https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/
IEEE
" » I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest)." Rafa | Sciencx [Online]. Available: https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/. [Accessed: ]
rf:citation
» I Don’t Always Test My Code… But When I Do, I Do It in Prod (or With Kotest) | Rafa | Sciencx | https://www.scien.cx/2025/08/25/i-dont-always-test-my-code-but-when-i-do-i-do-it-in-prod-or-with-kotest/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.