Careeroza
Testing Flows and ViewModels
Main Dispatcher Extension
viewModelScope uses Dispatchers.Main, which does not exist in local JVM tests. Replace it before each test with a small JUnit 5 extension:
class MainDispatcherExtension(
val dispatcher: TestDispatcher = UnconfinedTestDispatcher(),
) : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext) = Dispatchers.setMain(dispatcher)
override fun afterEach(context: ExtensionContext) = Dispatchers.resetMain()
}ViewModel Test with a Fake
class FakeProductRepository : ProductRepository {
var failNext = false
override suspend fun getProducts(): List<Product> {
delay(100)
if (failNext) throw IOException("offline")
return listOf(Product(1, "Atta 5kg", 289))
}
override suspend fun addToCart(id: Long) {}
}
@ExtendWith(MainDispatcherExtension::class)
class ProductsViewModelTest {
private val repo = FakeProductRepository()
@Test
fun `shows products after loading`() = runTest {
val vm = ProductsViewModel(repo)
assertEquals(ProductsUiState.Loading, vm.uiState.value)
advanceUntilIdle()
assertEquals(ProductsUiState.Success(listOf(Product(1, "Atta 5kg", 289))), vm.uiState.value)
}
@Test
fun `shows error when offline`() = runTest {
repo.failNext = true
val vm = ProductsViewModel(repo)
advanceUntilIdle()
assertTrue(vm.uiState.value is ProductsUiState.Error)
}
}Flows with Turbine
Turbine makes flow assertions readable: awaitItem() suspends until the next emission and fails the test after a timeout.
@Test
fun `cart total updates when items are added`() = runTest {
val store = CartStore(catalog)
store.state.test {
assertEquals(0, awaitItem().total)
store.add("milk")
assertEquals(30, awaitItem().total)
store.add("bread")
assertEquals(75, awaitItem().total)
cancelAndIgnoreRemainingEvents()
}
}
// Without Turbine: collect into a list in backgroundScope
@Test
fun `collects all values`() = runTest {
val values = mutableListOf<Int>()
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { counter.toList(values) }
counter.value = 1
counter.value = 2
assertEquals(listOf(0, 1, 2), values)
}