Skip to content

Commit

Permalink
Update Turbine 0.13.0 -> 1.1.0 and add runMviTest to combine runTest …
Browse files Browse the repository at this point in the history
…with turbineScope

(cherry picked from commit aff875374fb1a2a047b729019df973198b67e3de)
  • Loading branch information
wmontwe committed Oct 10, 2024
1 parent 588033f commit 0cb8576
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 540 deletions.
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
package app.k9mail.core.ui.compose.testing.mvi

import app.cash.turbine.testIn
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
import assertk.assertions.assertThatAndTurbinesConsumed
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.CoroutineScope

suspend fun <STATE, EVENT, EFFECT> eventStateTest(
/**
* Tests that the state of the [viewModel] changes as expected when the [event] is sent.
*/
suspend inline fun <reified STATE, EVENT, EFFECT> MviContext.eventStateTest(
viewModel: UnidirectionalViewModel<STATE, EVENT, EFFECT>,
initialState: STATE,
event: EVENT,
expectedState: STATE,
coroutineScope: CoroutineScope,
) {
val stateTurbine = viewModel.state.testIn(coroutineScope)
val effectTurbine = viewModel.effect.testIn(coroutineScope)
val turbines = listOf(stateTurbine, effectTurbine)

assertThatAndTurbinesConsumed(
actual = stateTurbine.awaitItem(),
turbines = turbines,
) {
isEqualTo(initialState)
}
val turbines = turbinesWithInitialStateCheck(viewModel, initialState)

viewModel.event(event)

assertThatAndTurbinesConsumed(
actual = stateTurbine.awaitItem(),
turbines = turbines,
) {
isEqualTo(expectedState)
}
assertThat(turbines.stateTurbine.awaitItem()).isEqualTo(expectedState)
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,73 @@
package app.k9mail.core.ui.compose.testing.mvi

import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.testIn
import app.cash.turbine.TurbineContext
import app.cash.turbine.turbineScope
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
import assertk.Assert
import assertk.all
import assertk.assertThat
import assertk.assertions.isEqualTo
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest

/**
* The `runMviTest` function is a wrapper around `runTest` and `turbineScope`
* that provides a MviContext to the test body.
*/
fun runMviTest(
testBody: suspend MviContext.() -> Unit,
) {
runTest {
val testScope = this
turbineScope {
val turbineContext = this
testBody(
DefaultMviContext(
testScope = testScope,
turbineContext = turbineContext,
),
)
}
}
}

interface MviContext {
val testScope: TestScope
val turbineContext: TurbineContext
}

class DefaultMviContext(
override val testScope: TestScope,
override val turbineContext: TurbineContext,
) : MviContext

@OptIn(ExperimentalCoroutinesApi::class)
fun MviContext.advanceUntilIdle() {
testScope.advanceUntilIdle()
}

/**
* The `turbines` extension function creates a MviTurbines instance for the given MVI ViewModel.
*/
inline fun <reified STATE, EVENT, EFFECT> TestScope.turbines(
inline fun <reified STATE, EVENT, EFFECT> MviContext.turbines(
viewModel: UnidirectionalViewModel<STATE, EVENT, EFFECT>,
): MviTurbines<STATE, EFFECT> {
return MviTurbines(
stateTurbine = viewModel.state.testIn(backgroundScope),
effectTurbine = viewModel.effect.testIn(backgroundScope),
)
with(turbineContext) {
return MviTurbines(
stateTurbine = viewModel.state.testIn(testScope.backgroundScope),
effectTurbine = viewModel.effect.testIn(testScope.backgroundScope),
)
}
}

/**
* The `turbinesWithInitialStateCheck` extension function creates a MviTurbines instance for the given MVI ViewModel
* and ensures that the initial state is emitted.
*/
suspend inline fun <reified STATE, EVENT, EFFECT> TestScope.turbinesWithInitialStateCheck(
suspend inline fun <reified STATE, EVENT, EFFECT> MviContext.turbinesWithInitialStateCheck(
viewModel: UnidirectionalViewModel<STATE, EVENT, EFFECT>,
initialState: STATE,
): MviTurbines<STATE, EFFECT> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app.k9mail.feature.account.edit.ui.server.settings.modify

import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.runMviTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountState
Expand All @@ -17,7 +18,6 @@ import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test

Expand All @@ -27,7 +27,7 @@ class ModifyIncomingServerSettingsViewModelTest {
val mainDispatcherRule = MainDispatcherRule()

@Test
fun `should load account state from use case`() = runTest {
fun `should load account state from use case`() = runMviTest {
val accountUuid = "accountUuid"
val accountState = AccountState(
uuid = "accountUuid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app.k9mail.feature.account.edit.ui.server.settings.modify

import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.runMviTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountState
Expand All @@ -16,7 +17,6 @@ import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test

Expand All @@ -26,7 +26,7 @@ class ModifyOutgoingServerSettingsViewModelTest {
val mainDispatcherRule = MainDispatcherRule()

@Test
fun `should load account state from use case`() = runTest {
fun `should load account state from use case`() = runMviTest {
val accountUuid = "accountUuid"
val accountState = AccountState(
uuid = "accountUuid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app.k9mail.feature.account.edit.ui.server.settings.save
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndEffectTurbineConsumed
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndStateTurbineConsumed
import app.k9mail.core.ui.compose.testing.mvi.runMviTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Effect
Expand All @@ -12,7 +13,6 @@ import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSetting
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNotNull
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test

Expand All @@ -22,7 +22,7 @@ class BaseSaveServerSettingsViewModelTest {
val mainDispatcherRule = MainDispatcherRule()

@Test
fun `should save server settings when SaveServerSettings event received and emit NavigateNext`() = runTest {
fun `should save server settings when SaveServerSettings event received and emit NavigateNext`() = runMviTest {
var recordedAccountUuid: String? = null
var recordedIsIncoming: Boolean? = null
val testSubject = TestSaveServerSettingsViewModel(
Expand All @@ -49,7 +49,7 @@ class BaseSaveServerSettingsViewModelTest {
}

@Test
fun `should set error state when save settings failed`() = runTest {
fun `should set error state when save settings failed`() = runMviTest {
val testSubject = TestSaveServerSettingsViewModel(
accountUuid = ACCOUNT_UUID,
saveServerSettings = { _, _ ->
Expand All @@ -71,7 +71,7 @@ class BaseSaveServerSettingsViewModelTest {
}

@Test
fun `should allow NavigateBack when error and not loading`() = runTest {
fun `should allow NavigateBack when error and not loading`() = runMviTest {
val failure = Failure.SaveServerSettingsFailed("Test exception")
val testSubject = TestSaveServerSettingsViewModel(
accountUuid = ACCOUNT_UUID,
Expand Down
Loading

0 comments on commit 0cb8576

Please sign in to comment.