Skip to content

Commit 6860644

Browse files
committedAug 19, 2024·
rename consumable state And start viewmodel testing
1 parent ae7f457 commit 6860644

File tree

8 files changed

+169
-24
lines changed

8 files changed

+169
-24
lines changed
 

‎core/state/src/main/kotlin/br/com/mob1st/core/state/managers/ConsumableManager.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ interface ConsumableManager<State> {
1616
/**
1717
* The state flow that holds the consumable side effects.
1818
*/
19-
val consumableUiState: StateFlow<State>
19+
val consumablesState: StateFlow<State>
2020

2121
/**
2222
* Consumes the side effect of the given [lens].
@@ -38,7 +38,7 @@ class ConsumableDelegate<T : Any>(
3838
initialValue: T,
3939
) : ConsumableManager<T>,
4040
MutableStateFlow<T> by MutableStateFlow(initialValue) {
41-
override val consumableUiState: StateFlow<T> = asStateFlow()
41+
override val consumablesState: StateFlow<T> = asStateFlow()
4242

4343
override fun <Property> consume(lens: Lens<T, Property?>) {
4444
update {

‎features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/builder/intro/BuilderIntroPage.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal fun BuilderIntroPage(
3737
) {
3838
val viewModel = koinViewModel<BuilderIntroViewModel>()
3939
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
40-
val consumables by viewModel.consumableUiState.collectAsStateWithLifecycle()
40+
val consumables by viewModel.consumablesState.collectAsStateWithLifecycle()
4141
val snackbarHostState = remember {
4242
SnackbarHostState()
4343
}

‎features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/builder/steps/BudgetBuilderStepPage.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal fun BudgetBuilderStepPage(
4545
parametersOf(args)
4646
}
4747
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
48-
val consumables by viewModel.consumableUiState.collectAsStateWithLifecycle()
48+
val consumables by viewModel.consumablesState.collectAsStateWithLifecycle()
4949
CategoryBuilderStepScreen(
5050
uiState = uiState,
5151
consumables = consumables,

‎features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/builder/steps/BudgetBuilderStepViewModel.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal class BudgetBuilderStepViewModel(
3636
private val isLoadingState = AsyncLoadingState()
3737
private val step = args.toStep()
3838

39-
override val consumableUiState: StateFlow<BuilderStepConsumables> = consumableDelegate.asStateFlow()
39+
override val consumablesState: StateFlow<BuilderStepConsumables> = consumableDelegate.asStateFlow()
4040
override val uiState: StateFlow<BudgetBuilderStepUiState> =
4141
initState()
4242
.catchIn(errorHandler)

‎features/finances/impl/src/main/kotlin/br/com/mob1st/features/finances/impl/ui/category/detail/CategoryDetailPage.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ internal fun CategoryDetailPage(
4646
parameters = { parametersOf(args) },
4747
)
4848
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
49-
val consumables by viewModel.consumableUiState.collectAsStateWithLifecycle()
49+
val consumables by viewModel.consumablesState.collectAsStateWithLifecycle()
5050
val snackbarHostState = remember {
5151
SnackbarHostState()
5252
}

‎features/finances/impl/src/test/kotlin/br/com/mob1st/features/finances/impl/ui/builder/BudgetBuilderStepViewModelTest.kt

+14-14
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class BudgetBuilderStepViewModelTest {
8282
val viewModel = viewModel(testScheduler, args)
8383
val expected = BuilderStepConsumables()
8484
// When
85-
viewModel.consumableUiState.test {
85+
viewModel.consumablesState.test {
8686
// Then
8787
assertEquals(expected, awaitItem())
8888
}
@@ -98,7 +98,7 @@ class BudgetBuilderStepViewModelTest {
9898
)
9999
turbineScope {
100100
viewModel.uiState.drop(1).testIn(backgroundScope)
101-
val receiveConsumables = viewModel.consumableUiState.testIn(backgroundScope)
101+
val receiveConsumables = viewModel.consumablesState.testIn(backgroundScope)
102102
assertEquals(expected, receiveConsumables.awaitItem())
103103
}
104104
}
@@ -114,7 +114,7 @@ class BudgetBuilderStepViewModelTest {
114114
)
115115
turbineScope {
116116
viewModel.uiState.testIn(backgroundScope).skipItems(1)
117-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
117+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
118118
viewModel.addNewCategory()
119119
assertEquals(expected, receiveConsumable.awaitItem())
120120
}
@@ -134,7 +134,7 @@ class BudgetBuilderStepViewModelTest {
134134
val viewModel = viewModel(testScheduler, args)
135135
turbineScope {
136136
val receiveUiState = viewModel.uiState.testIn(backgroundScope)
137-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
137+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
138138
val body = receiveUiState.awaitItem().body as BuilderStepLoadedBody
139139
val category = body.manuallyAdded[randomIndex].category
140140
viewModel.selectManuallyAddedItem(randomIndex)
@@ -166,7 +166,7 @@ class BudgetBuilderStepViewModelTest {
166166
val viewModel = viewModel(testScheduler, args)
167167
turbineScope {
168168
val receiveUiState = viewModel.uiState.testIn(backgroundScope)
169-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
169+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
170170
val body = receiveUiState.awaitItem().body as BuilderStepLoadedBody
171171
val category = body.suggestions[randomIndex].category
172172
viewModel.selectSuggestedItem(randomIndex)
@@ -209,7 +209,7 @@ class BudgetBuilderStepViewModelTest {
209209
)
210210
turbineScope {
211211
viewModel.uiState.drop(1).testIn(backgroundScope)
212-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
212+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
213213
viewModel.addNewCategory()
214214
assertEquals(expectedWhenSelectCategory, receiveConsumable.awaitItem())
215215
viewModel.typeCategoryName("a")
@@ -230,7 +230,7 @@ class BudgetBuilderStepViewModelTest {
230230
)
231231
turbineScope {
232232
viewModel.uiState.drop(1).testIn(backgroundScope)
233-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
233+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
234234
viewModel.submitCategoryName()
235235
assertEquals(expected, receiveConsumable.awaitItem())
236236
}
@@ -252,7 +252,7 @@ class BudgetBuilderStepViewModelTest {
252252
)
253253
turbineScope {
254254
viewModel.uiState.drop(1).testIn(backgroundScope)
255-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
255+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
256256
viewModel.selectSuggestedItem(1)
257257
assertEquals(expected, receiveConsumable.awaitItem())
258258
}
@@ -274,7 +274,7 @@ class BudgetBuilderStepViewModelTest {
274274
)
275275
turbineScope {
276276
viewModel.uiState.drop(1).testIn(backgroundScope)
277-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
277+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
278278
viewModel.selectManuallyAddedItem(2)
279279
assertEquals(expected, receiveConsumable.awaitItem())
280280
}
@@ -301,7 +301,7 @@ class BudgetBuilderStepViewModelTest {
301301
)
302302
turbineScope {
303303
viewModel.uiState.drop(1).testIn(backgroundScope)
304-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
304+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
305305
viewModel.next()
306306
assertEquals(expected, receiveConsumable.awaitItem())
307307
}
@@ -320,7 +320,7 @@ class BudgetBuilderStepViewModelTest {
320320
)
321321
turbineScope {
322322
viewModel.uiState.drop(1).testIn(backgroundScope)
323-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
323+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
324324
viewModel.next()
325325
assertEquals(expected, receiveConsumable.awaitItem())
326326
}
@@ -333,7 +333,7 @@ class BudgetBuilderStepViewModelTest {
333333
val viewModel = viewModel(testScheduler, args)
334334
turbineScope {
335335
viewModel.uiState.drop(1).testIn(backgroundScope)
336-
val receiveConsumable = viewModel.consumableUiState.testIn(backgroundScope)
336+
val receiveConsumable = viewModel.consumablesState.testIn(backgroundScope)
337337
viewModel.next()
338338
assertEquals(BuilderStepConsumables(), receiveConsumable.awaitItem())
339339
}
@@ -349,7 +349,7 @@ class BudgetBuilderStepViewModelTest {
349349
)
350350
turbineScope {
351351
viewModel.uiState.drop(1).testIn(backgroundScope)
352-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
352+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
353353
viewModel.selectManuallyAddedItem(0)
354354
assertEquals(expected, receiveConsumable.awaitItem())
355355
}
@@ -365,7 +365,7 @@ class BudgetBuilderStepViewModelTest {
365365
)
366366
turbineScope {
367367
viewModel.uiState.drop(1).testIn(backgroundScope)
368-
val receiveConsumable = viewModel.consumableUiState.drop(1).testIn(backgroundScope)
368+
val receiveConsumable = viewModel.consumablesState.drop(1).testIn(backgroundScope)
369369
viewModel.selectSuggestedItem(0)
370370
assertEquals(expected, receiveConsumable.awaitItem())
371371
}

‎features/finances/impl/src/test/kotlin/br/com/mob1st/features/finances/impl/ui/builder/intro/BuilderIntroViewModelTest.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal class BuilderIntroViewModelTest {
3636
val viewModel = initViewModel()
3737
turbineScope {
3838
val receiveUiState = viewModel.uiState.testIn(backgroundScope)
39-
val receiveConsumables = viewModel.consumableUiState.testIn(backgroundScope)
39+
val receiveConsumables = viewModel.consumablesState.testIn(backgroundScope)
4040
assertEquals(
4141
BuilderIntroUiState(false),
4242
receiveUiState.awaitItem(),
@@ -69,7 +69,7 @@ internal class BuilderIntroViewModelTest {
6969
viewModel.start()
7070
turbineScope {
7171
val receiveUiState = viewModel.uiState.testIn(backgroundScope)
72-
val receiveConsumables = viewModel.consumableUiState.testIn(backgroundScope)
72+
val receiveConsumables = viewModel.consumablesState.testIn(backgroundScope)
7373
assertEquals(
7474
BuilderIntroUiState(false),
7575
receiveUiState.awaitItem(),
@@ -88,7 +88,7 @@ internal class BuilderIntroViewModelTest {
8888
viewModel.start()
8989
turbineScope {
9090
val receiveUiState = viewModel.uiState.testIn(backgroundScope)
91-
val receiveConsumables = viewModel.consumableUiState.testIn(backgroundScope)
91+
val receiveConsumables = viewModel.consumablesState.testIn(backgroundScope)
9292
assertEquals(
9393
BuilderIntroUiState(false),
9494
receiveUiState.awaitItem(),
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,155 @@
11
package br.com.mob1st.features.finances.impl.ui.category.detail
22

3+
import app.cash.turbine.test
4+
import app.cash.turbine.turbineScope
5+
import br.com.mob1st.core.kotlinx.coroutines.DefaultCoroutineDispatcher
6+
import br.com.mob1st.core.kotlinx.structures.Money
7+
import br.com.mob1st.core.state.managers.ConsumableDelegate
8+
import br.com.mob1st.features.finances.impl.domain.entities.CalculatorPreferences
9+
import br.com.mob1st.features.finances.impl.domain.entities.CategoryDetail
10+
import br.com.mob1st.features.finances.impl.domain.fixtures.categoryDetail
11+
import br.com.mob1st.features.finances.impl.domain.usecases.GetCategoryDetailUseCase
12+
import br.com.mob1st.features.finances.impl.domain.usecases.SetCalculatorPreferencesUseCase
13+
import br.com.mob1st.features.finances.impl.domain.usecases.SetCategoryUseCase
14+
import br.com.mob1st.features.finances.impl.ui.fixtures.categoryEntry
15+
import br.com.mob1st.tests.featuresutils.MainDispatcherTestExtension
16+
import io.kotest.property.Arb
17+
import io.kotest.property.arbitrary.bind
18+
import io.kotest.property.arbitrary.next
19+
import io.mockk.every
20+
import io.mockk.mockk
21+
import kotlinx.coroutines.ExperimentalCoroutinesApi
22+
import kotlinx.coroutines.flow.Flow
23+
import kotlinx.coroutines.flow.MutableStateFlow
24+
import kotlinx.coroutines.flow.emptyFlow
25+
import kotlinx.coroutines.flow.flowOf
26+
import kotlinx.coroutines.test.TestDispatcher
27+
import kotlinx.coroutines.test.TestScope
28+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
29+
import kotlinx.coroutines.test.runTest
30+
import org.junit.jupiter.api.BeforeEach
331
import org.junit.jupiter.api.Test
32+
import org.junit.jupiter.api.extension.ExtendWith
33+
import kotlin.test.assertEquals
34+
import kotlin.test.assertFalse
435

36+
@ExtendWith(MainDispatcherTestExtension::class)
537
class CategoryViewModelTest {
38+
private lateinit var getDetail: GetCategoryDetailUseCase
39+
private lateinit var setCategory: SetCategoryUseCase
40+
private lateinit var setPreferencesUseCase: SetCalculatorPreferencesUseCase
41+
private lateinit var categoryStateHandleTest: CategoryStateHandle
42+
43+
@BeforeEach
44+
fun setUp() {
45+
getDetail = mockk()
46+
setCategory = mockk(relaxed = true)
47+
setPreferencesUseCase = mockk(relaxed = true)
48+
categoryStateHandleTest = mockk()
49+
}
50+
51+
@Test
52+
fun `GIVEN arguments And a saved state WHEN get initial state THEN saved data is used`() = runTest {
53+
val detail = Arb.categoryDetail().next()
54+
val entry = CategoryEntry(detail.category)
55+
givenInitialState(flowOf(detail), MutableStateFlow(entry))
56+
val viewModel = initViewModel()
57+
val expectedUiState = CategoryDetailUiState.Loaded(
58+
detail = detail,
59+
entry = entry,
60+
)
61+
turbineScope {
62+
val receivedUiState = viewModel.uiState.testIn(backgroundScope)
63+
val receivedConsumables = viewModel.consumablesState.testIn(backgroundScope)
64+
val actualUiState = receivedUiState.awaitItem()
65+
val actualConsumables = receivedConsumables.awaitItem()
66+
assertEquals(expectedUiState, actualUiState)
67+
assertEquals(CategoryDetailConsumables(), actualConsumables)
68+
}
69+
}
70+
71+
@Test
72+
fun `GIVEN a zeroed state WHEN get initial state THEN assert loading state is emitted`() = runTest {
73+
every { getDetail[any()] } returns emptyFlow()
74+
val viewModel = initViewModel()
75+
viewModel.uiState.test {
76+
val state = awaitItem()
77+
assertEquals(CategoryDetailUiState.Loading, state)
78+
}
79+
}
80+
81+
@Test
82+
fun `GIVEN a number WHEN type THEN assert ui state changed And new value is saved`() = runTest {
83+
val detail = Arb.categoryDetail().next().copy(
84+
preferences = CalculatorPreferences(isEditCentsEnabled = false),
85+
)
86+
val entry = Arb.categoryEntry().next().copy(amount = Money.Zero)
87+
givenInitialState(flowOf(detail), MutableStateFlow(entry))
88+
val viewModel = initViewModel()
89+
viewModel.uiState.test {
90+
skipItems(1)
91+
viewModel.type(1)
92+
val state = awaitItem()
93+
val expectedEntry = entry.copy(
94+
amount = Money(100),
95+
)
96+
assertEquals(CategoryDetailUiState.Loaded(detail, expectedEntry), state)
97+
}
98+
}
99+
100+
@Test
101+
fun `GIVEN a state with number WHEN type THEN assert value is erased`() = runTest {
102+
val detail = Arb.categoryDetail().next().copy()
103+
val entry = Arb.categoryEntry().next().copy(amount = Money(1))
104+
givenInitialState(flowOf(detail), MutableStateFlow(entry))
105+
val viewModel = initViewModel()
106+
viewModel.uiState.test {
107+
skipItems(1)
108+
viewModel.deleteNumber()
109+
val state = awaitItem()
110+
val expectedEntry = entry.copy(
111+
amount = Money.Zero,
112+
)
113+
assertEquals(CategoryDetailUiState.Loaded(detail, expectedEntry), state)
114+
}
115+
}
116+
6117
@Test
7118
fun test() {
8-
assert(CategoryViewModel::class.equals(""))
119+
// test show enter name dialog
120+
// test show icon picker dialog
121+
// test show all recurrences dialog
122+
// test type category name
123+
// test submit dialogs
124+
// test enable/disable edit cents
125+
// test submission
126+
assertFalse(true)
127+
}
128+
129+
private fun givenInitialState(
130+
categoryDetailFlow: Flow<CategoryDetail> = flowOf(Arb.categoryDetail().next()),
131+
entryFlow: MutableStateFlow<CategoryEntry> = MutableStateFlow(Arb.categoryEntry().next()),
132+
) {
133+
every { getDetail[any()] } returns categoryDetailFlow
134+
every { categoryStateHandleTest.entry(any()) } returns entryFlow
135+
every { categoryStateHandleTest.update(any()) } answers {
136+
entryFlow.value = firstArg()
137+
}
9138
}
139+
140+
@OptIn(ExperimentalCoroutinesApi::class)
141+
private fun TestScope.initViewModel(
142+
args: CategoryDetailArgs = Arb.bind<CategoryDetailArgs>().next(),
143+
testDispatcher: TestDispatcher = UnconfinedTestDispatcher(testScheduler),
144+
) = CategoryViewModel(
145+
default = DefaultCoroutineDispatcher(
146+
testDispatcher,
147+
),
148+
consumableDelegate = ConsumableDelegate(CategoryDetailConsumables()),
149+
categoryStateHandle = categoryStateHandleTest,
150+
getCategoryDetail = getDetail,
151+
setCategory = setCategory,
152+
setPreferences = setPreferencesUseCase,
153+
args = args,
154+
)
10155
}

0 commit comments

Comments
 (0)
Please sign in to comment.