Add BaseViewModel to abstract state and effect setup
This commit is contained in:
parent
1d93ffb89f
commit
2b2745ba9c
2 changed files with 113 additions and 0 deletions
|
@ -0,0 +1,57 @@
|
|||
package app.k9mail.core.ui.compose.common.mvi
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* An abstract base ViewModel that implements [UnidirectionalViewModel] and provides basic
|
||||
* functionality for managing state and effects.
|
||||
*
|
||||
* @param STATE The type that represents the state of the ViewModel. For example, the UI state of a screen can be
|
||||
* represented as a state.
|
||||
* @param EVENT The type that represents user actions that can occur and should be handled by the ViewModel. For
|
||||
* example, a button click can be represented as an event.
|
||||
* @param EFFECT The type that represents side-effects that can occur in response to the state changes. For example,
|
||||
* a navigation event can be represented as an effect.
|
||||
*
|
||||
* @property initialState The initial [STATE] of the ViewModel.
|
||||
*/
|
||||
abstract class BaseViewModel<STATE, EVENT, EFFECT>(
|
||||
initialState: STATE,
|
||||
) : ViewModel(),
|
||||
UnidirectionalViewModel<STATE, EVENT, EFFECT> {
|
||||
|
||||
private val _state = MutableStateFlow(initialState)
|
||||
override val state: StateFlow<STATE> = _state.asStateFlow()
|
||||
|
||||
private val _effect = MutableSharedFlow<EFFECT>()
|
||||
override val effect: SharedFlow<EFFECT> = _effect.asSharedFlow()
|
||||
|
||||
/**
|
||||
* Updates the [STATE] of the ViewModel.
|
||||
*
|
||||
* @param update A function that takes the current [STATE] and produces a new [STATE].
|
||||
*/
|
||||
protected fun updateState(update: (STATE) -> STATE) {
|
||||
_state.update(update)
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a side effect.
|
||||
*
|
||||
* @param effect The [EFFECT] to emit.
|
||||
*/
|
||||
protected fun emitEffect(effect: EFFECT) {
|
||||
viewModelScope.launch {
|
||||
_effect.emit(effect)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package app.k9mail.core.ui.compose.common.mvi
|
||||
|
||||
import app.cash.turbine.test
|
||||
import app.k9mail.core.ui.compose.testing.MainDispatcherRule
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class BaseViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
@Test
|
||||
fun `should emit initial state`() = runTest {
|
||||
val viewModel = TestBaseViewModel()
|
||||
assertThat(viewModel.state.value).isEqualTo("Initial state")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should update state`() = runTest {
|
||||
val viewModel = TestBaseViewModel()
|
||||
|
||||
viewModel.event("Test event")
|
||||
|
||||
assertThat(viewModel.state.value).isEqualTo("Test event")
|
||||
|
||||
viewModel.event("Another test event")
|
||||
|
||||
assertThat(viewModel.state.value).isEqualTo("Another test event")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit effects`() = runTest {
|
||||
val viewModel = TestBaseViewModel()
|
||||
|
||||
viewModel.effect.test {
|
||||
viewModel.event("Test effect")
|
||||
|
||||
assertThat(awaitItem()).isEqualTo("Test effect")
|
||||
|
||||
viewModel.event("Another test effect")
|
||||
|
||||
assertThat(awaitItem()).isEqualTo("Another test effect")
|
||||
}
|
||||
}
|
||||
|
||||
private class TestBaseViewModel : BaseViewModel<String, String, String>("Initial state") {
|
||||
override fun event(event: String) {
|
||||
updateState { event }
|
||||
emitEffect(event)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue