Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 32abbbb

Browse files
Finish Flow tests for db
1 parent 5c05386 commit 32abbbb

File tree

8 files changed

+283
-66
lines changed

8 files changed

+283
-66
lines changed

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter2_network/PostRemoteViewModel.kt‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ class PostNetworkViewModel(
1414
private val postsUseCase: PostRemoteUseCase
1515
) : ViewModel() {
1616

17-
18-
val testInt = MutableLiveData<Int>()
19-
2017
/**
2118
* LiveData to test network operation with [Post]
2219
*/
@@ -71,17 +68,14 @@ class PostNetworkViewModel(
7168
}
7269
}
7370

74-
7571
fun onClick(post: Post) {
7672

7773
}
7874

79-
8075
override fun onCleared() {
8176
super.onCleared()
8277
coroutineScope.cancel()
8378
}
84-
8579
}
8680

8781
class PostViewModelFactory :

‎Tutorial2-1FlowBasics/src/main/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter3_database/PostDBUseCase.kt‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.flow
99
import kotlinx.coroutines.flow.flowOn
1010
import kotlinx.coroutines.flow.map
1111

12-
1312
class PostDBUseCase(
1413
private val postDBRepository: PostDBRepository,
1514
private val entityToPostMapper: EntityToPostMapper,

‎Tutorial2-1FlowBasics/src/test-shared/java/com/smarttoolfactory/tutorial2_1flowbasics/flow/FlowTestUtil.kt‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.collect
88
import kotlinx.coroutines.launch
99

1010

11-
class TestObserver<T>(
11+
class FlowTestObserver<T>(
1212
private val coroutineScope: CoroutineScope,
1313
private val flow: Flow<T>
1414
) {
@@ -19,14 +19,14 @@ class TestObserver<T>(
1919
flow.collect { testValues.add(it) }
2020
}
2121

22-
fun assertNoValues(): TestObserver<T> {
22+
fun assertNoValues(): FlowTestObserver<T> {
2323
if (testValues.isNotEmpty()) throw AssertionException(
2424
"Assertion error with actual size ${testValues.size}"
2525
)
2626
return this
2727
}
2828

29-
fun assertValueCount(count: Int): TestObserver<T> {
29+
fun assertValueCount(count: Int): FlowTestObserver<T> {
3030
if (count < 0) throw AssertionException(
3131
"Assertion error! value count cannot be smaller than zero"
3232
)
@@ -36,10 +36,8 @@ class TestObserver<T>(
3636
return this
3737
}
3838

39-
fun assertValues(vararg predicates: T): TestObserver<T> {
40-
41-
if (!testValues.containsAll(predicates.asList())) throw Exception("Assertion error")
42-
39+
fun assertValues(vararg predicates: T): FlowTestObserver<T> {
40+
if (!testValues.containsAll(predicates.asList())) throw AssertionException("Assertion error some ")
4341
return this
4442
}
4543

@@ -53,12 +51,12 @@ class TestObserver<T>(
5351
// return this
5452
// }
5553

56-
fun assertValues(predicate: (List<T>) -> Boolean): TestObserver<T> {
54+
fun assertValues(predicate: (List<T>) -> Boolean): FlowTestObserver<T> {
5755
predicate(testValues)
5856
return this
5957
}
6058

61-
fun values(predicate: (List<T>) -> Unit): TestObserver<T> {
59+
fun values(predicate: (List<T>) -> Unit): FlowTestObserver<T> {
6260
predicate(testValues)
6361
return this
6462
}
@@ -73,6 +71,6 @@ class TestObserver<T>(
7371

7472
}
7573

76-
fun <T> Flow<T>.test(scope: CoroutineScope): TestObserver<T> {
77-
return TestObserver(scope, this)
74+
fun <T> Flow<T>.test(scope: CoroutineScope): FlowTestObserver<T> {
75+
return FlowTestObserver(scope, this)
7876
}

‎Tutorial2-1FlowBasics/src/test-shared/java/com/smarttoolfactory/tutorial2_1flowbasics/livedata/LiveDataTestUtil.kt‎

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,15 @@ fun <T> LiveData<T>.getOrAwaitValue(
5353
* * Use with `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
5454
*
5555
*/
56-
class TestObserver<T>(
57-
private val liveData: LiveData<T>,
58-
private val time: Long = 2,
59-
private val timeUnit: TimeUnit = TimeUnit.SECONDS,
60-
private val valueCount: Int = -1
61-
) : Observer<T> {
6256

63-
private val latch: CountDownLatch = if (valueCount > 0) {
64-
CountDownLatch(valueCount)
65-
} else {
66-
CountDownLatch(1)
67-
}
57+
/*
58+
TODO Add observe forever and timeout mechanism
59+
60+
Hint: Make other functions wait while onChanged is still running
61+
*/
62+
class LiveDataTestObserver<T> constructor(
63+
private val liveData: LiveData<T>
64+
) : Observer<T> {
6865

6966
init {
7067
liveData.observeForever(this)
@@ -73,28 +70,17 @@ class TestObserver<T>(
7370
private val testValues = mutableListOf<T>()
7471

7572
override fun onChanged(t: T) {
76-
7773
if (t != null) testValues.add(t)
78-
79-
// if (valueCount > 0) {
80-
// latch.countDown()
81-
// }
82-
//
83-
// // Don't wait indefinitely if the LiveData is not set.
84-
// if (!latch.await(time, timeUnit)) {
85-
// clear()
86-
// throw TimeoutException("LiveData value was never set.")
87-
// }
8874
}
8975

90-
fun assertNoValues(): TestObserver<T> {
76+
fun assertNoValues(): LiveDataTestObserver<T> {
9177
if (testValues.isNotEmpty()) throw AssertionException(
9278
"Assertion error with actual size ${testValues.size}"
9379
)
9480
return this
9581
}
9682

97-
fun assertValueCount(count: Int): TestObserver<T> {
83+
fun assertValueCount(count: Int): LiveDataTestObserver<T> {
9884
if (count < 0) throw AssertionException(
9985
"Assertion error! value count cannot be smaller than zero"
10086
)
@@ -104,8 +90,8 @@ class TestObserver<T>(
10490
return this
10591
}
10692

107-
fun assertValues(vararg predicates: T): TestObserver<T> {
108-
if (!testValues.containsAll(predicates.asList())) throw Exception("Assertion error")
93+
fun assertValues(vararg predicates: T): LiveDataTestObserver<T> {
94+
if (!testValues.containsAll(predicates.asList())) throw AssertionException("Assertion error!")
10995
return this
11096
}
11197

@@ -119,12 +105,12 @@ class TestObserver<T>(
119105
// return this
120106
// }
121107

122-
fun assertValues(predicate: (List<T>) -> Boolean): TestObserver<T> {
108+
fun assertValues(predicate: (List<T>) -> Boolean): LiveDataTestObserver<T> {
123109
predicate(testValues)
124110
return this
125111
}
126112

127-
fun values(predicate: (List<T>) -> Unit): TestObserver<T> {
113+
fun values(predicate: (List<T>) -> Unit): LiveDataTestObserver<T> {
128114
predicate(testValues)
129115
return this
130116
}
@@ -149,9 +135,9 @@ class TestObserver<T>(
149135
}
150136
}
151137

152-
fun <T> LiveData<T>.test(): TestObserver<T> {
138+
fun <T> LiveData<T>.test(): LiveDataTestObserver<T> {
153139

154-
val testObserver = TestObserver(this)
140+
val testObserver = LiveDataTestObserver(this)
155141

156142
// Remove this testObserver that is added in init block of TestObserver, and clears previous data
157143
testObserver.clear()

‎Tutorial2-1FlowBasics/src/test/java/com/smarttoolfactory/tutorial2_1flowbasics/chapter2_network/PostNetworkViewModelTest.kt‎

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import com.smarttoolfactory.tutorial2_1flowbasics.convertFromJsonToObjectList
88
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.Post
99
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.Status
1010
import com.smarttoolfactory.tutorial2_1flowbasics.getResourceAsText
11-
import com.smarttoolfactory.tutorial2_1flowbasics.livedata.getOrAwaitValue
11+
import com.smarttoolfactory.tutorial2_1flowbasics.livedata.LiveDataTestObserver
1212
import com.smarttoolfactory.tutorial2_1flowbasics.livedata.test
1313
import io.mockk.clearMocks
1414
import io.mockk.every
@@ -38,7 +38,7 @@ class PostNetworkViewModelTest {
3838

3939

4040
/**
41-
* Test for testing LiveData util [TestObserver]
41+
* Test for testing LiveData util [LiveDataTestObserver]
4242
*/
4343
@Test
4444
fun test() {
@@ -48,18 +48,18 @@ class PostNetworkViewModelTest {
4848
val testObserver = myTestData.test()
4949

5050
// WHEN
51-
// myTestData.value = 1
52-
// myTestData.value = 2
53-
// myTestData.value = 3
51+
myTestData.value = 1
52+
myTestData.value = 2
53+
myTestData.value = 3
5454

5555
// THEN
56-
myTestData.getOrAwaitValue()
57-
// testObserver
58-
// .assertValues { list ->
59-
// (list[0] == 1 && list[1] == 2 && list[2] == 3)
60-
// }
61-
// .assertValueCount(3)
62-
// .dispose()
56+
testObserver
57+
.assertValues { list ->
58+
(list[0] ==1&& list[1] ==2&& list[2] ==3)
59+
}
60+
.assertValueCount(3)
61+
// 🔥 Do not forget to dispose
62+
.dispose()
6363

6464
// 🔥 Do not forget to dispose
6565
// testObserver.dispose()
@@ -73,6 +73,7 @@ class PostNetworkViewModelTest {
7373
every { useCase.getPostFlow() } returns flow<List<Post>> {
7474
emit(throw Exception("Network Exception"))
7575
}
76+
7677
val testObserver = postNetworkViewModel.postViewState.test()
7778

7879
// WHEN
@@ -81,31 +82,40 @@ class PostNetworkViewModelTest {
8182
// THEN
8283
testObserver
8384
.assertValues { states ->
84-
8585
(states[0].status == Status.LOADING &&
8686
states[1].status == Status.ERROR)
8787
}
88+
.dispose()
8889

8990

9091
// THEN
9192
val finalState = testObserver.values()[1]
92-
9393
Truth.assertThat("Network Exception").isEqualTo(finalState?.error?.message)
9494
Truth.assertThat(finalState?.error).isInstanceOf(Exception::class.java)
9595
verify(atMost = 1) { useCase.getPostFlow() }
96-
testObserver.dispose()
9796
}
9897

9998
@Test
10099
fun `given data retrieved from useCase, should have ViewState with SUCCESS and data`() =
101100
testCoroutineRule.runBlockingTest {
102101

103102
// GIVEN
103+
every { useCase.getPostFlow() } returns flow<List<Post>> {
104+
emit(postList)
105+
}
106+
val testObserver = postNetworkViewModel.postViewState.test()
104107

105108
// WHEN
109+
postNetworkViewModel.getPosts()
106110

107111
// THEN
112+
val viewStates = testObserver.values()
113+
Truth.assertThat(viewStates.first().status).isEqualTo(Status.LOADING)
108114

115+
val actual = viewStates.last().data
116+
Truth.assertThat(actual?.size).isEqualTo(100)
117+
verify(atMost = 1) { useCase.getPostFlow() }
118+
testObserver.dispose()
109119
}
110120

111121
@Before
@@ -117,5 +127,4 @@ class PostNetworkViewModelTest {
117127
fun tearDown() {
118128
clearMocks(useCase)
119129
}
120-
121130
}
Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,89 @@
11
package com.smarttoolfactory.tutorial2_1flowbasics.chapter3_database
22

3-
class PostDBRepositoryTest {
3+
import com.google.common.truth.Truth
4+
import com.smarttoolfactory.tutorial2_1flowbasics.chapter2_network.BaseCoroutineJUnit5Test
5+
import com.smarttoolfactory.tutorial2_1flowbasics.convertFromJsonToObjectList
6+
import com.smarttoolfactory.tutorial2_1flowbasics.data.db.PostDao
7+
import com.smarttoolfactory.tutorial2_1flowbasics.data.model.PostEntity
8+
import com.smarttoolfactory.tutorial2_1flowbasics.flow.test
9+
import com.smarttoolfactory.tutorial2_1flowbasics.getResourceAsText
10+
import io.mockk.clearMocks
11+
import io.mockk.every
12+
import io.mockk.mockk
13+
import kotlinx.coroutines.channels.Channel
14+
import kotlinx.coroutines.flow.consumeAsFlow
15+
import kotlinx.coroutines.flow.flow
16+
import kotlinx.coroutines.test.runBlockingTest
17+
import org.junit.jupiter.api.AfterEach
18+
import org.junit.jupiter.api.BeforeEach
19+
import org.junit.jupiter.api.Test
20+
import org.junit.jupiter.api.TestInstance
421

22+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
23+
class PostDBRepositoryTest : BaseCoroutineJUnit5Test() {
24+
25+
private val postDao = mockk<PostDao>()
26+
private lateinit var postDBRepository: PostDBRepository
27+
28+
private val postEntityList by lazy {
29+
convertFromJsonToObjectList<PostEntity>(getResourceAsText("response.json"))!!
30+
}
31+
32+
@Test
33+
fun testFlowObserver() = testCoroutineScope.runBlockingTest {
34+
35+
val subject = Channel<Int>()
36+
val observer = subject.consumeAsFlow().test(this)
37+
38+
observer.assertNoValues()
39+
subject.send(0)
40+
observer.assertValues(0)
41+
42+
observer.dispose()
43+
}
44+
45+
@Test
46+
fun `given empty list returned from DB, should return empty list`() =
47+
testCoroutineScope.runBlockingTest {
48+
49+
// GIVEN
50+
every { postDBRepository.getPostFlow() } returns flow { emit(listOf()) }
51+
52+
// WHEN
53+
val testObserver = postDBRepository.getPostFlow().test(this)
54+
55+
// THEN
56+
val actual = testObserver.values()[0]
57+
Truth.assertThat(actual.size).isEqualTo(0)
58+
// testObserver.dispose()
59+
}
60+
61+
@Test
62+
fun `given data list returned from DB, should return data list`() =
63+
testCoroutineScope.runBlockingTest {
64+
65+
// GIVEN
66+
every { postDBRepository.getPostFlow() } returns flow { emit(postEntityList) }
67+
68+
// WHEN
69+
val testObserver = postDBRepository.getPostFlow().test(this)
70+
71+
// THEN
72+
val actual = testObserver.values()[0]
73+
Truth.assertThat(actual.size).isEqualTo(100)
74+
Truth.assertThat(actual).containsExactlyElementsIn(postEntityList)
75+
testObserver.dispose()
76+
}
77+
78+
@BeforeEach
79+
override fun setUp() {
80+
super.setUp()
81+
postDBRepository = PostDBRepository(postDao)
82+
}
83+
84+
@AfterEach
85+
override fun tearDown() {
86+
super.tearDown()
87+
clearMocks(postDao)
88+
}
589
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /