Skip to content

Commit 92737b5

Browse files
committed
DashboardViewModel unit tests
Signed-off-by: Pablo <[email protected]>
1 parent 63d471c commit 92737b5

File tree

5 files changed

+208
-10
lines changed

5 files changed

+208
-10
lines changed

app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardMobileActivityTest.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.dhis2.android.rtsm.utils.NetworkUtils
1212
import org.dhis2.commons.filters.FilterManager
1313
import org.dhis2.commons.prefs.PreferenceProvider
1414
import org.dhis2.commons.resources.ResourceManager
15+
import org.dhis2.commons.viewmodel.DispatcherProvider
1516
import org.dhis2.ui.ThemeManager
1617
import org.dhis2.usescases.teiDashboard.DashboardRepositoryImpl
1718
import org.dhis2.usescases.teiDashboard.DashboardViewModel
@@ -50,6 +51,8 @@ class TeiDashboardMobileActivityTest {
5051
private var repository: DashboardRepositoryImpl = mock {
5152

5253
}
54+
55+
private var dispatcher:DispatcherProvider = mock()
5356
var tei = Observable.just(
5457
TrackedEntityInstance.builder()
5558
.uid(TEI_Uid)
@@ -87,7 +90,8 @@ class TeiDashboardMobileActivityTest {
8790
private fun initViewModel() {
8891
viewModel = DashboardViewModel(
8992
repository,
90-
analyticsHelper
93+
analyticsHelper,
94+
dispatcher,
9195
)
9296

9397
}

app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModel.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import androidx.lifecycle.LiveData
44
import androidx.lifecycle.MutableLiveData
55
import androidx.lifecycle.ViewModel
66
import androidx.lifecycle.viewModelScope
7-
import kotlinx.coroutines.Dispatchers
87
import kotlinx.coroutines.async
98
import kotlinx.coroutines.flow.MutableStateFlow
109
import kotlinx.coroutines.flow.asStateFlow
1110
import kotlinx.coroutines.launch
11+
import org.dhis2.commons.viewmodel.DispatcherProvider
1212
import org.dhis2.utils.AuthorityException
1313
import org.dhis2.utils.analytics.ACTIVE_FOLLOW_UP
1414
import org.dhis2.utils.analytics.AnalyticsHelper
@@ -21,6 +21,7 @@ import timber.log.Timber
2121
class DashboardViewModel(
2222
private val repository: DashboardRepository,
2323
private val analyticsHelper: AnalyticsHelper,
24+
private val dispatcher: DispatcherProvider,
2425
) : ViewModel() {
2526

2627
private val eventUid = MutableLiveData<String>()
@@ -52,8 +53,8 @@ class DashboardViewModel(
5253
}
5354

5455
private fun fetchDashboardModel() {
55-
viewModelScope.launch {
56-
val result = async(context = Dispatchers.IO) {
56+
viewModelScope.launch(dispatcher.io()) {
57+
val result = async {
5758
repository.getDashboardModel()
5859
}
5960
try {
@@ -74,8 +75,8 @@ class DashboardViewModel(
7475
}
7576

7677
private fun fetchGrouping() {
77-
viewModelScope.launch {
78-
val result = async(context = Dispatchers.IO) {
78+
viewModelScope.launch(dispatcher.io()) {
79+
val result = async {
7980
repository.getGrouping()
8081
}
8182
try {
@@ -110,7 +111,7 @@ class DashboardViewModel(
110111
repository.setFollowUp((dashboardModel.value as DashboardEnrollmentModel).currentEnrollment.uid())
111112
_syncNeeded.value = true
112113
_state.value = State.TO_UPDATE
113-
analyticsHelper.setEvent(ACTIVE_FOLLOW_UP, _showFollowUpBar.toString(), FOLLOW_UP)
114+
analyticsHelper.setEvent(ACTIVE_FOLLOW_UP, _showFollowUpBar.value.toString(), FOLLOW_UP)
114115
updateDashboard()
115116
}
116117
}
@@ -137,8 +138,8 @@ class DashboardViewModel(
137138
}
138139

139140
fun deleteEnrollment(onAuthorityError: () -> Unit) {
140-
viewModelScope.launch {
141-
val result = async(context = Dispatchers.IO) {
141+
viewModelScope.launch(dispatcher.io()) {
142+
val result = async {
142143
dashboardModel.value.takeIf { it is DashboardEnrollmentModel }?.let {
143144
repository.deleteEnrollmentIfPossible((it as DashboardEnrollmentModel).currentEnrollment.uid())
144145
.blockingGet()

app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardViewModelFactory.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@ package org.dhis2.usescases.teiDashboard
22

33
import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.ViewModelProvider
5+
import org.dhis2.commons.viewmodel.DispatcherProvider
56
import org.dhis2.utils.analytics.AnalyticsHelper
67

78
@Suppress("UNCHECKED_CAST")
89
class DashboardViewModelFactory(
910
val repository: DashboardRepository,
1011
val analyticsHelper: AnalyticsHelper,
12+
val dispatcher: DispatcherProvider,
1113
) : ViewModelProvider.Factory {
1214

1315
override fun <T : ViewModel> create(modelClass: Class<T>): T {
1416
return DashboardViewModel(
1517
repository,
1618
analyticsHelper,
19+
dispatcher,
1720
) as T
1821
}
1922
}

app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardModule.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.dhis2.commons.di.dagger.PerActivity
77
import org.dhis2.commons.matomo.MatomoAnalyticsController
88
import org.dhis2.commons.prefs.PreferenceProvider
99
import org.dhis2.commons.schedulers.SchedulerProvider
10+
import org.dhis2.commons.viewmodel.DispatcherProvider
1011
import org.dhis2.data.forms.EnrollmentFormRepository
1112
import org.dhis2.data.forms.FormRepository
1213
import org.dhis2.data.forms.dataentry.EnrollmentRuleEngineRepository
@@ -123,7 +124,8 @@ class TeiDashboardModule(
123124
fun providesViewModelFactory(
124125
repository: DashboardRepository,
125126
analyticsHelper: AnalyticsHelper,
127+
dispatcher: DispatcherProvider,
126128
): DashboardViewModelFactory {
127-
return DashboardViewModelFactory(repository, analyticsHelper)
129+
return DashboardViewModelFactory(repository, analyticsHelper, dispatcher)
128130
}
129131
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package org.dhis2.usescases.teiDashboard
2+
3+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4+
import io.reactivex.Observable
5+
import kotlinx.coroutines.CoroutineDispatcher
6+
import kotlinx.coroutines.Dispatchers
7+
import kotlinx.coroutines.ExperimentalCoroutinesApi
8+
import kotlinx.coroutines.test.StandardTestDispatcher
9+
import kotlinx.coroutines.test.setMain
10+
import org.dhis2.commons.viewmodel.DispatcherProvider
11+
import org.dhis2.utils.analytics.ACTIVE_FOLLOW_UP
12+
import org.dhis2.utils.analytics.AnalyticsHelper
13+
import org.dhis2.utils.analytics.FOLLOW_UP
14+
import org.hisp.dhis.android.core.common.State
15+
import org.hisp.dhis.android.core.enrollment.Enrollment
16+
import org.hisp.dhis.android.core.enrollment.EnrollmentStatus
17+
import org.junit.Assert.assertTrue
18+
import org.junit.Before
19+
import org.junit.Rule
20+
import org.junit.Test
21+
import org.mockito.kotlin.any
22+
import org.mockito.kotlin.doReturn
23+
import org.mockito.kotlin.mock
24+
import org.mockito.kotlin.verify
25+
import org.mockito.kotlin.whenever
26+
27+
class DashboardViewModelTest {
28+
29+
@get:Rule
30+
val instantExecutorRule = InstantTaskExecutorRule()
31+
32+
private val repository: DashboardRepository = mock()
33+
private val analyticsHelper: AnalyticsHelper = mock()
34+
private val testingDispatcher = StandardTestDispatcher()
35+
36+
@OptIn(ExperimentalCoroutinesApi::class)
37+
@Before
38+
fun setUp() {
39+
Dispatchers.setMain(testingDispatcher)
40+
}
41+
42+
@Test
43+
fun shouldFetchEnrollmentModel() {
44+
mockEnrollmentModel()
45+
mockGrouping(true)
46+
47+
val dashboardViewModel = getViewModel()
48+
49+
with(dashboardViewModel) {
50+
assertTrue(dashboardModel.value == mockedEnrollmentModel)
51+
assertTrue(showFollowUpBar.value)
52+
assertTrue(!syncNeeded.value)
53+
assertTrue(showStatusBar.value == EnrollmentStatus.ACTIVE)
54+
assertTrue(state.value == State.SYNCED)
55+
}
56+
}
57+
58+
@Test
59+
fun shouldFetchTeiModel() {
60+
mockTeiModel()
61+
mockGrouping(false)
62+
63+
val dashboardViewModel = getViewModel()
64+
65+
with(dashboardViewModel) {
66+
assertTrue(dashboardModel.value == mockedTeiModel)
67+
assertTrue(!showFollowUpBar.value)
68+
assertTrue(!syncNeeded.value)
69+
assertTrue(showStatusBar.value == null)
70+
assertTrue(state.value == null)
71+
}
72+
}
73+
74+
@Test
75+
fun shouldSetGrouping() {
76+
mockEnrollmentModel()
77+
mockGrouping(false)
78+
79+
val dashboardViewModel = getViewModel()
80+
81+
with(dashboardViewModel) {
82+
assertTrue(groupByStage.value == false)
83+
setGrouping(true)
84+
verify(repository).setGrouping(true)
85+
assertTrue(groupByStage.value == true)
86+
}
87+
}
88+
89+
@Test
90+
fun shouldUpdateEventUid() {
91+
mockEnrollmentModel()
92+
mockGrouping(false)
93+
94+
with(getViewModel()) {
95+
assertTrue(eventUid().value == null)
96+
updateEventUid("eventUid")
97+
assertTrue(eventUid().value == "eventUid")
98+
}
99+
}
100+
101+
@Test
102+
fun shouldSetFollowUpOnEnrollment() {
103+
mockEnrollmentModel()
104+
mockGrouping(false)
105+
106+
with(getViewModel()) {
107+
onFollowUp()
108+
verify(repository).setFollowUp("enrollmentUid")
109+
assertTrue(state.value == State.TO_UPDATE)
110+
verify(analyticsHelper).setEvent(ACTIVE_FOLLOW_UP, "false", FOLLOW_UP)
111+
}
112+
}
113+
114+
@Test
115+
fun shouldUpdateEnrollmentStatus() {
116+
mockEnrollmentModel()
117+
mockGrouping(false)
118+
119+
with(getViewModel()) {
120+
whenever(repository.updateEnrollmentStatus(any(), any())) doReturn Observable.just(
121+
StatusChangeResultCode.CHANGED,
122+
)
123+
updateEnrollmentStatus(EnrollmentStatus.COMPLETED)
124+
verify(repository).updateEnrollmentStatus("enrollmentUid", EnrollmentStatus.COMPLETED)
125+
assertTrue(showStatusBar.value == EnrollmentStatus.COMPLETED)
126+
assertTrue(syncNeeded.value)
127+
assertTrue(state.value == State.TO_UPDATE)
128+
assertTrue(updateEnrollment.value == true)
129+
}
130+
}
131+
132+
@Test
133+
fun shouldShowMessageIfErrorWhileUpdatingEnrollmentStatus() {
134+
mockEnrollmentModel()
135+
mockGrouping(false)
136+
137+
with(getViewModel()) {
138+
whenever(repository.updateEnrollmentStatus(any(), any())) doReturn Observable.just(
139+
StatusChangeResultCode.FAILED,
140+
)
141+
updateEnrollmentStatus(EnrollmentStatus.COMPLETED)
142+
assertTrue(showStatusErrorMessages.value == StatusChangeResultCode.FAILED)
143+
}
144+
}
145+
146+
private fun getViewModel() = DashboardViewModel(
147+
repository,
148+
analyticsHelper,
149+
object :
150+
DispatcherProvider {
151+
override fun io(): CoroutineDispatcher {
152+
return testingDispatcher
153+
}
154+
155+
override fun computation(): CoroutineDispatcher {
156+
return testingDispatcher
157+
}
158+
159+
override fun ui(): CoroutineDispatcher {
160+
return testingDispatcher
161+
}
162+
},
163+
).also {
164+
testingDispatcher.scheduler.advanceUntilIdle()
165+
}
166+
167+
private fun mockEnrollmentModel() {
168+
whenever(repository.getDashboardModel()) doReturn mockedEnrollmentModel
169+
whenever(mockedEnrollmentModel.currentEnrollment) doReturn mockedEnrollment
170+
}
171+
172+
private fun mockTeiModel() {
173+
whenever(repository.getDashboardModel()) doReturn mockedTeiModel
174+
}
175+
176+
private fun mockGrouping(group: Boolean) {
177+
whenever(repository.getGrouping()) doReturn group
178+
}
179+
180+
private val mockedEnrollmentModel: DashboardEnrollmentModel = mock()
181+
private val mockedTeiModel: DashboardTEIModel = mock()
182+
private val mockedEnrollment: Enrollment = mock {
183+
on { uid() } doReturn "enrollmentUid"
184+
on { followUp() } doReturn true
185+
on { aggregatedSyncState() } doReturn State.SYNCED
186+
on { status() } doReturn EnrollmentStatus.ACTIVE
187+
}
188+
}

0 commit comments

Comments
 (0)