Flow testing with Turbine

Say hello to Turbine 1.0, our library for testing kotlinx.coroutines Flow and more.

Turbine changes push-based Flows into pull-based suspend functions to simplify testing.

mealsFlow.test {
  assertEquals(Meal.Breakfast, awaitItem())
  assertEquals(Meal.Lunch, awaitItem())
  assertEquals(Meal.Dinner, awaitItem())
  awaitComplete()
}

Each awaitItem() or awaitComplete() call suspends until the desired event arrives. If a different event occurs or a timeout is reached the functions throw an AssertionError and fail your test. You can also wait for errors, skip items, cancel the Flow, and more.

In addition to its use with Flow, standalone Turbines can be created to adapt other push-based mechanisms like callbacks for testing.

class FakeLogger : Logger {
  val messages = Turbine<String>()

  override fun log(message: String) {
    messages += message
  }
}

The standalone Turbine offers the same API as the test function for pulling out values.

val logger = FakeLogger()

val userService = UserService(logger)
assertNull(userService.get(4552))

with(logger.messages) {
  assertEquals("HTTP --> /user/4552 GET", awaitItem())
  assertEquals("HTTP <-- /user/4552 404 Not Found", awaitItem())
}

As your testing needs grow, the library can help with utilities for multiple Turbines, multiple Flows, sharing timeouts, aggregating errors, and more.

This post is part of Cash App’s Summer of Kotlin Multiplatform series.