Cash App Jun 21, 2021

Sharp Edges in Kotlin Coroutines Testing Tools

M10 Related OWASP risk: Insufficient Cryptography Learn more →

Article Summary

Kevin Cianfarini from Cash App discovered a sneaky threading bug that stumped his entire team. The culprit? A misunderstood feature in Kotlin's coroutine testing tools that silently breaks thread assertions.

While migrating Cash App's MVP architecture to Kotlin coroutines, the team hit a confusing wall: their tests were failing because TestCoroutineDispatcher wasn't behaving as expected. What seemed like a simple withContext() fix turned into a deep dive into how 'immediate' dispatchers actually work.

Key Takeaways

Critical Insight

TestCoroutineDispatcher's 'immediate' behavior means it won't respect thread switching in withContext(), breaking common testing assumptions about dispatcher behavior.

The fix required understanding a subtle distinction in coroutine dispatcher documentation that even experienced developers commonly misinterpret.

About This Article

Problem

Cash App's MVP architecture test expected Thread[main @coroutine#1,5,main] but got Thread[test,5,main]. The issue was that TestCoroutineDispatcher's immediate mode runs tasks on the current thread instead of switching dispatcher context as expected.

Solution

Kevin Cianfarini's team fixed this by replacing thread-level assertions with CoroutineDispatcher assertions using currentCoroutineContext()[CoroutineDispatcher.Key]. When thread verification was necessary, they switched to standard runBlocking instead.

Impact

The fix stopped silent test failures. It clarified that 'immediate' in coroutine dispatchers means skipping CoroutineDispatcher.dispatch calls entirely, not enabling manual virtual clock control. The team had misread the documentation initially.