Skip to content

Commit 22fd588

Browse files
committed
Migrate from RxJava to Kotlin Flow
1 parent 76fb9f6 commit 22fd588

File tree

17 files changed

+94
-80
lines changed

17 files changed

+94
-80
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ For a comprehensive guide on how to follow this methodology, please refer to the
1111
## Setup
1212

1313
1. Clone the repository
14-
2. Examine the structure of the tests in the `ChatViewModelSpec.kt` file.
14+
2. Checkout the `main` branch for Kotlin Flow or `rxjava` for RxJava
15+
3. Examine the structure of the tests in the `ChatViewModelSpec.kt` file.
1516

1617
## Libraries Used
1718

1819
- Dependency Injection: [Koin](https://github.com/InsertKoinIO/koin)
1920
- Testing Framework: [Kotest](https://github.com/kotest/kotest)
21+
- Reactive Framework: Both [Kotlin Flow](https://github.com/Kotlin/kotlinx.coroutines) and [RxJava](https://github.com/ReactiveX/RxJava) are supported
2022

2123
## Contact
2224

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ dependencies {
7777
testImplementation(libs.rxjava)
7878
testImplementation(platform(libs.junit.jupiter.bom))
7979
testImplementation(libs.junit.jupiter.api)
80+
testImplementation(libs.kotlinx.coroutines.test)
8081

8182
testRuntimeOnly(libs.junit.jupiter.engine)
8283

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.steliosf.unittestingdiet.common
2+
3+
import kotlinx.coroutines.CoroutineDispatcher
4+
5+
interface CoroutineDispatcherProvider {
6+
val main: CoroutineDispatcher
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.steliosf.unittestingdiet.common
2+
3+
import kotlinx.coroutines.CoroutineDispatcher
4+
import kotlinx.coroutines.Dispatchers
5+
6+
class DefaultCoroutineDispatcherProvider : CoroutineDispatcherProvider {
7+
override val main: CoroutineDispatcher = Dispatchers.Main
8+
}

app/src/main/java/com/steliosf/unittestingdiet/data/datasource/ChatDataSource.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package com.steliosf.unittestingdiet.data.datasource
22

33
import com.steliosf.unittestingdiet.domain.model.Message
44
import com.steliosf.unittestingdiet.domain.model.User
5-
import io.reactivex.rxjava3.core.Completable
6-
import io.reactivex.rxjava3.core.Single
75

86
interface ChatDataSource {
9-
fun getChatMessages(user: User): Single<List<Message>>
10-
fun sendChatMessage(message: Message, user: User): Completable
7+
suspend fun getChatMessages(user: User): List<Message>
8+
suspend fun sendChatMessage(message: Message, user: User)
119
}

app/src/main/java/com/steliosf/unittestingdiet/data/repository/ChatMessagesRepository.kt

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,25 @@ package com.steliosf.unittestingdiet.data.repository
33
import com.steliosf.unittestingdiet.data.datasource.ChatDataSource
44
import com.steliosf.unittestingdiet.domain.model.Message
55
import com.steliosf.unittestingdiet.domain.model.User
6-
import io.reactivex.rxjava3.core.Completable
7-
import io.reactivex.rxjava3.core.Observable
8-
import io.reactivex.rxjava3.subjects.BehaviorSubject
6+
import kotlinx.coroutines.flow.Flow
7+
import kotlinx.coroutines.flow.MutableStateFlow
8+
import kotlinx.coroutines.flow.emitAll
9+
import kotlinx.coroutines.flow.flow
910

1011
class ChatMessagesRepository(
1112
private val chatDataSource: ChatDataSource
1213
) {
14+
private val _cachedChatMessages = MutableStateFlow<List<Message>>(emptyList())
1315

14-
private val cachedChatMessages: BehaviorSubject<List<Message>> = BehaviorSubject.create()
15-
16-
fun getChatMessages(user: User): Observable<List<Message>> {
17-
return if (cachedChatMessages.hasValue()) cachedChatMessages else {
18-
loadExistingChatMessages(user).andThen(cachedChatMessages)
16+
fun getChatMessages(user: User): Flow<List<Message>> = flow {
17+
if (_cachedChatMessages.value.isEmpty()) {
18+
_cachedChatMessages.emit(chatDataSource.getChatMessages(user))
1919
}
20+
emitAll(_cachedChatMessages)
2021
}
2122

22-
fun sendChatMessage(message: Message, user: User): Completable {
23-
return chatDataSource.sendChatMessage(message, user)
24-
.doOnSubscribe {
25-
cachedChatMessages.onNext(getCurrentMessagesList() + message)
26-
}.doOnComplete { /* .. */ }
27-
}
28-
29-
private fun loadExistingChatMessages(user: User): Completable {
30-
return chatDataSource.getChatMessages(user)
31-
.doOnSuccess {
32-
cachedChatMessages.onNext(it)
33-
}.ignoreElement()
34-
}
35-
36-
private fun getCurrentMessagesList(): List<Message> {
37-
return cachedChatMessages.value ?: emptyList()
23+
suspend fun sendChatMessage(message: Message, user: User) {
24+
_cachedChatMessages.apply { value = value + message }
25+
chatDataSource.sendChatMessage(message, user)
3826
}
3927
}

app/src/main/java/com/steliosf/unittestingdiet/di/AppModule.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.steliosf.unittestingdiet.di
22

3+
import com.steliosf.unittestingdiet.common.CoroutineDispatcherProvider
4+
import com.steliosf.unittestingdiet.common.DefaultCoroutineDispatcherProvider
35
import com.steliosf.unittestingdiet.data.repository.ChatMessagesRepository
46
import com.steliosf.unittestingdiet.domain.usecase.GetChatMessagesUseCase
57
import com.steliosf.unittestingdiet.domain.usecase.SendTextMessageUseCase
@@ -14,6 +16,7 @@ val appModule = module {
1416

1517
factoryOf(::GetChatMessagesUseCase)
1618
factoryOf(::SendTextMessageUseCase)
19+
factoryOf<CoroutineDispatcherProvider>(::DefaultCoroutineDispatcherProvider)
1720

1821
singleOf(::ChatMessagesRepository)
1922
}

app/src/main/java/com/steliosf/unittestingdiet/domain/usecase/GetChatMessagesUseCase.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package com.steliosf.unittestingdiet.domain.usecase
33
import com.steliosf.unittestingdiet.data.repository.ChatMessagesRepository
44
import com.steliosf.unittestingdiet.domain.model.Message
55
import com.steliosf.unittestingdiet.domain.model.User
6-
import io.reactivex.rxjava3.core.Observable
6+
import kotlinx.coroutines.flow.Flow
7+
import kotlinx.coroutines.flow.map
78

89
class GetChatMessagesUseCase(
910
private val chatMessagesRepository: ChatMessagesRepository
1011
) {
1112

12-
operator fun invoke(user: User): Observable<List<Message>> {
13+
operator fun invoke(user: User): Flow<List<Message>> {
1314
return chatMessagesRepository.getChatMessages(user).map { messagesList ->
1415
messagesList.sortedBy { it.date.time }
1516
}

app/src/main/java/com/steliosf/unittestingdiet/domain/usecase/SendTextMessageUseCase.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
package com.steliosf.unittestingdiet.domain.usecase
22

3-
import com.steliosf.unittestingdiet.domain.model.User
4-
import com.steliosf.unittestingdiet.domain.model.error.InvalidMessageException
53
import com.steliosf.unittestingdiet.data.repository.ChatMessagesRepository
64
import com.steliosf.unittestingdiet.domain.factory.MessageFactory
7-
import io.reactivex.rxjava3.core.Completable
5+
import com.steliosf.unittestingdiet.domain.model.User
6+
import com.steliosf.unittestingdiet.domain.model.error.InvalidMessageException
87

98
class SendTextMessageUseCase(
109
private val chatMessagesRepository: ChatMessagesRepository
1110
) {
1211

13-
operator fun invoke(text: String, user: User): Completable {
14-
return if (isValid(text)) {
12+
suspend operator fun invoke(text: String, user: User) {
13+
if (isValid(text)) {
1514
val message = MessageFactory.fromText(text)
1615
chatMessagesRepository.sendChatMessage(message, user)
1716
} else {
18-
Completable.error(InvalidMessageException("Message is too long"))
17+
throw InvalidMessageException("Message is too long")
1918
}
2019
}
2120

app/src/main/java/com/steliosf/unittestingdiet/presentation/viewmodel/BaseViewModel.kt

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)