Change test setup to generalised event state testing
This commit is contained in:
parent
fe86ef5878
commit
1f18130b5c
6 changed files with 86 additions and 61 deletions
|
@ -20,6 +20,13 @@ internal class AccountOptionsViewModel(
|
|||
initialState: State = State(),
|
||||
private val validator: Validator,
|
||||
) : BaseViewModel<State, Event, Effect>(initialState), ViewModel {
|
||||
|
||||
override fun initState(state: State) {
|
||||
updateState {
|
||||
state.copy()
|
||||
}
|
||||
}
|
||||
|
||||
override fun event(event: Event) {
|
||||
when (event) {
|
||||
is OnAccountNameChanged -> updateState { state ->
|
||||
|
@ -63,12 +70,6 @@ internal class AccountOptionsViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
override fun initState(state: State) {
|
||||
updateState {
|
||||
state.copy()
|
||||
}
|
||||
}
|
||||
|
||||
private fun submit() = with(state.value) {
|
||||
val accountNameResult = validator.validateAccountName(accountName.value)
|
||||
val displayNameResult = validator.validateDisplayName(displayName.value)
|
||||
|
@ -82,9 +83,9 @@ internal class AccountOptionsViewModel(
|
|||
|
||||
updateState {
|
||||
it.copy(
|
||||
accountName = state.value.accountName.updateFromValidationResult(accountNameResult),
|
||||
displayName = state.value.displayName.updateFromValidationResult(displayNameResult),
|
||||
emailSignature = state.value.emailSignature.updateFromValidationResult(emailSignatureResult),
|
||||
accountName = it.accountName.updateFromValidationResult(accountNameResult),
|
||||
displayName = it.displayName.updateFromValidationResult(displayNameResult),
|
||||
emailSignature = it.emailSignature.updateFromValidationResult(emailSignatureResult),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package app.k9mail.feature.account.setup.testing
|
||||
|
||||
import app.cash.turbine.testIn
|
||||
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
|
||||
import assertk.assertions.assertThatAndTurbinesConsumed
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
internal suspend fun <STATE, EVENT, EFFECT> 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)
|
||||
}
|
||||
|
||||
viewModel.event(event)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(expectedState)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ import app.k9mail.feature.account.setup.ui.options.FakeAccountOptionsViewModel
|
|||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -35,7 +34,7 @@ class AccountSetupScreenKtTest : ComposeTest() {
|
|||
}
|
||||
|
||||
for (step in SetupStep.values()) {
|
||||
viewModel.mutableState.update { it.copy(setupStep = step) }
|
||||
viewModel.state { it.copy(setupStep = step) }
|
||||
onNodeWithTag(getTagForStep(step)).assertExists()
|
||||
}
|
||||
}
|
||||
|
@ -62,12 +61,12 @@ class AccountSetupScreenKtTest : ComposeTest() {
|
|||
assertThat(onFinishCounter).isEqualTo(0)
|
||||
assertThat(onBackCounter).isEqualTo(0)
|
||||
|
||||
viewModel.mutableEffect.emit(Effect.NavigateNext)
|
||||
viewModel.effect(Effect.NavigateNext)
|
||||
|
||||
assertThat(onFinishCounter).isEqualTo(1)
|
||||
assertThat(onBackCounter).isEqualTo(0)
|
||||
|
||||
viewModel.mutableEffect.emit(Effect.NavigateBack)
|
||||
viewModel.effect(Effect.NavigateBack)
|
||||
|
||||
assertThat(onFinishCounter).isEqualTo(1)
|
||||
assertThat(onBackCounter).isEqualTo(1)
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
package app.k9mail.feature.account.setup.ui
|
||||
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.AccountSetupContract.State
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
internal class FakeAccountSetupViewModel(
|
||||
initialState: State = State(),
|
||||
) : AccountSetupContract.ViewModel {
|
||||
) : BaseViewModel<State, Event, Effect>(initialState), AccountSetupContract.ViewModel {
|
||||
|
||||
val mutableState = MutableStateFlow(initialState)
|
||||
val mutableEffect = MutableSharedFlow<Effect>()
|
||||
val events = mutableListOf<Event>()
|
||||
|
||||
override val state: StateFlow<State> = mutableState.asStateFlow()
|
||||
override val effect: SharedFlow<Effect> = mutableEffect.asSharedFlow()
|
||||
fun state(update: (State) -> (State)) {
|
||||
updateState { update(it) }
|
||||
}
|
||||
|
||||
override fun event(event: Event) {
|
||||
events.add(event)
|
||||
}
|
||||
|
||||
fun effect(effect: Effect) {
|
||||
emitEffect(effect)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@ import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
|||
import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency
|
||||
import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount
|
||||
import app.k9mail.feature.account.setup.domain.input.StringInputField
|
||||
import app.k9mail.feature.account.setup.testing.eventStateTest
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.assertThatAndTurbinesConsumed
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
|
@ -24,9 +25,15 @@ class AccountOptionsViewModelTest {
|
|||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private val testSubject = AccountOptionsViewModel(
|
||||
validator = FakeAccountOptionsValidator(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should change state when OnAccountNameChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnAccountNameChanged("accountName"),
|
||||
expectedState = State(accountName = StringInputField(value = "accountName")),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -36,6 +43,8 @@ class AccountOptionsViewModelTest {
|
|||
@Test
|
||||
fun `should change state when OnDisplayNameChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnDisplayNameChanged("displayName"),
|
||||
expectedState = State(displayName = StringInputField(value = "displayName")),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -45,6 +54,8 @@ class AccountOptionsViewModelTest {
|
|||
@Test
|
||||
fun `should change state when OnEmailSignatureChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnEmailSignatureChanged("emailSignature"),
|
||||
expectedState = State(emailSignature = StringInputField(value = "emailSignature")),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -54,6 +65,8 @@ class AccountOptionsViewModelTest {
|
|||
@Test
|
||||
fun `should change state when OnCheckFrequencyChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnCheckFrequencyChanged(EmailCheckFrequency.EVERY_12_HOURS),
|
||||
expectedState = State(checkFrequency = EmailCheckFrequency.EVERY_12_HOURS),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -63,6 +76,8 @@ class AccountOptionsViewModelTest {
|
|||
@Test
|
||||
fun `should change state when OnMessageDisplayCountChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnMessageDisplayCountChanged(EmailDisplayCount.MESSAGES_1000),
|
||||
expectedState = State(messageDisplayCount = EmailDisplayCount.MESSAGES_1000),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -72,6 +87,8 @@ class AccountOptionsViewModelTest {
|
|||
@Test
|
||||
fun `should change state when OnShowNotificationChanged event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(),
|
||||
event = Event.OnShowNotificationChanged(true),
|
||||
expectedState = State(showNotification = true),
|
||||
coroutineScope = backgroundScope,
|
||||
|
@ -79,9 +96,9 @@ class AccountOptionsViewModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should update state and emit NavigateNext effect when OnNextClicked event is received and input valid`() =
|
||||
fun `should change state and emit NavigateNext effect when OnNextClicked event received and input valid`() =
|
||||
runTest {
|
||||
val viewModel = AccountOptionsViewModel(validator = FakeAccountOptionsValidator())
|
||||
val viewModel = testSubject
|
||||
val stateTurbine = viewModel.state.testIn(backgroundScope)
|
||||
val effectTurbine = viewModel.effect.testIn(backgroundScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
|
@ -107,12 +124,12 @@ class AccountOptionsViewModelTest {
|
|||
actual = effectTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(AccountOptionsContract.Effect.NavigateNext)
|
||||
isEqualTo(Effect.NavigateNext)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should update state and not emit effect when OnNextClicked event is received and input invalid`() =
|
||||
fun `should change state and not emit effect when OnNextClicked event received and input invalid`() =
|
||||
runTest {
|
||||
val viewModel = AccountOptionsViewModel(
|
||||
validator = FakeAccountOptionsValidator(
|
||||
|
@ -147,8 +164,8 @@ class AccountOptionsViewModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should emit NavigateBack effect when OnBackClicked event is received`() = runTest {
|
||||
val viewModel = AccountOptionsViewModel(validator = FakeAccountOptionsValidator())
|
||||
fun `should emit NavigateBack effect when OnBackClicked event received`() = runTest {
|
||||
val viewModel = testSubject
|
||||
val stateTurbine = viewModel.state.testIn(backgroundScope)
|
||||
val effectTurbine = viewModel.effect.testIn(backgroundScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
|
@ -166,34 +183,7 @@ class AccountOptionsViewModelTest {
|
|||
actual = effectTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(AccountOptionsContract.Effect.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun eventStateTest(
|
||||
event: Event,
|
||||
expectedState: State,
|
||||
coroutineScope: CoroutineScope,
|
||||
) {
|
||||
val viewModel = AccountOptionsViewModel(validator = FakeAccountOptionsValidator())
|
||||
val stateTurbine = viewModel.state.testIn(coroutineScope)
|
||||
val effectTurbine = viewModel.effect.testIn(coroutineScope)
|
||||
val turbines = listOf(stateTurbine, effectTurbine)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(State())
|
||||
}
|
||||
|
||||
viewModel.event(event)
|
||||
|
||||
assertThatAndTurbinesConsumed(
|
||||
actual = stateTurbine.awaitItem(),
|
||||
turbines = turbines,
|
||||
) {
|
||||
isEqualTo(expectedState)
|
||||
isEqualTo(Effect.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event
|
|||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State
|
||||
import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewModel
|
||||
|
||||
class FakeAccountOptionsViewModel(initialState: State = State()) :
|
||||
BaseViewModel<State, Event, Effect>(initialState), ViewModel {
|
||||
class FakeAccountOptionsViewModel(
|
||||
initialState: State = State(),
|
||||
) : BaseViewModel<State, Event, Effect>(initialState), ViewModel {
|
||||
|
||||
val events = mutableListOf<Event>()
|
||||
|
||||
|
|
Loading…
Reference in a new issue