-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathRocketsViewModelTest.kt
207 lines (171 loc) · 6.33 KB
/
RocketsViewModelTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package eu.krzdabrowski.starter.basicfeature.presentation
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import eu.krzdabrowski.starter.basicfeature.domain.usecase.GetRocketsUseCase
import eu.krzdabrowski.starter.basicfeature.domain.usecase.RefreshRocketsUseCase
import eu.krzdabrowski.starter.basicfeature.generateTestRocketFromDomain
import eu.krzdabrowski.starter.basicfeature.presentation.RocketsEvent.OpenWebBrowserWithDetails
import eu.krzdabrowski.starter.basicfeature.presentation.RocketsIntent.RefreshRockets
import eu.krzdabrowski.starter.basicfeature.presentation.RocketsIntent.RocketClicked
import eu.krzdabrowski.starter.basicfeature.presentation.mapper.toPresentationModel
import eu.krzdabrowski.starter.core.utils.MainDispatcherExtension
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.impl.annotations.SpyK
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class RocketsViewModelTest {
@JvmField
@RegisterExtension
val mainDispatcherExtension = MainDispatcherExtension()
@RelaxedMockK
private lateinit var getRocketsUseCase: GetRocketsUseCase
// there is some issue with mocking functional interface with kotlin.Result(Unit)
private lateinit var refreshRocketsUseCase: RefreshRocketsUseCase
@SpyK
private var savedStateHandle = SavedStateHandle()
private lateinit var objectUnderTest: RocketsViewModel
@BeforeEach
fun setUp() {
MockKAnnotations.init(this)
refreshRocketsUseCase = RefreshRocketsUseCase {
Result.success(Unit)
}
}
@Test
fun `should show loading state with no error state first during init rockets retrieval`() = runTest {
// Given
every { getRocketsUseCase() } returns emptyFlow()
setUpRocketsViewModel()
// When
// init
// Then
objectUnderTest.uiState.test {
val actualItem = awaitItem()
assertTrue(actualItem.isLoading)
assertFalse(actualItem.isError)
}
}
@Test
fun `should show loading state with no error state during rockets refresh`() = runTest {
// Given
every { getRocketsUseCase() } returns emptyFlow()
setUpRocketsViewModel()
// When
objectUnderTest.acceptIntent(RefreshRockets)
// Then
objectUnderTest.uiState.test {
val actualItem = awaitItem()
assertTrue(actualItem.isLoading)
assertFalse(actualItem.isError)
}
}
@Test
fun `should show fetched rockets with no loading & error state during init rockets retrieval success`() = runTest {
// Given
val testRocketsFromDomain = listOf(generateTestRocketFromDomain())
val testRocketsToPresentation = testRocketsFromDomain.map { it.toPresentationModel() }
every { getRocketsUseCase() } returns flowOf(
Result.success(testRocketsFromDomain),
)
setUpRocketsViewModel()
// When
// init
// Then
objectUnderTest.uiState.test {
val actualItem = awaitItem()
assertEquals(
expected = testRocketsToPresentation,
actual = actualItem.rockets,
)
assertFalse(actualItem.isLoading)
assertFalse(actualItem.isError)
}
}
@Test
fun `should show error state with no loading state during init rockets retrieval failure`() = runTest {
// Given
every { getRocketsUseCase() } returns flowOf(
Result.failure(IllegalStateException("Test error")),
)
setUpRocketsViewModel()
// When
// init
// Then
objectUnderTest.uiState.test {
val actualItem = awaitItem()
assertTrue(actualItem.isError)
assertFalse(actualItem.isLoading)
}
}
@Test
fun `should show error state with previously fetched rockets during rockets refresh failure`() = runTest {
// Given
val testRocketsFromDomain = listOf(generateTestRocketFromDomain())
val testRocketsToPresentation = testRocketsFromDomain.map { it.toPresentationModel() }
every { getRocketsUseCase() } returns flowOf(
Result.success(testRocketsFromDomain),
)
refreshRocketsUseCase = RefreshRocketsUseCase {
Result.failure(IllegalStateException("Test error"))
}
setUpRocketsViewModel()
// When
objectUnderTest.acceptIntent(RefreshRockets)
// Then
objectUnderTest.uiState.test {
skipItems(1) // Loading-Refreshing state
val actualItem = awaitItem()
assertTrue(actualItem.isError)
assertEquals(
expected = testRocketsToPresentation,
actual = actualItem.rockets,
)
}
}
@Test
fun `should open web browser if link has proper prefix`() = runTest {
// Given
val testUri = "https://testrocket.com"
every { getRocketsUseCase() } returns emptyFlow()
setUpRocketsViewModel()
// When
objectUnderTest.acceptIntent(RocketClicked(testUri))
// Then
objectUnderTest.getEvents().test {
assertEquals(
expected = OpenWebBrowserWithDetails(testUri),
actual = awaitItem(),
)
}
}
@Test
fun `should not open web browser if link is incorrect`() = runTest {
// Given
val testUri = "incorrectlink.com"
every { getRocketsUseCase() } returns emptyFlow()
setUpRocketsViewModel()
// When
objectUnderTest.acceptIntent(RocketClicked(testUri))
// Then
objectUnderTest.getEvents().test {
expectNoEvents()
}
}
private fun setUpRocketsViewModel(initialUiState: RocketsUiState = RocketsUiState()) {
objectUnderTest = RocketsViewModel(
getRocketsUseCase,
refreshRocketsUseCase,
savedStateHandle,
initialUiState,
)
}
}