Skip to content

Commit 423b63a

Browse files
authored
Merge pull request #301 from ptkNktq/refactor/usecase_and_reps
UseCase/Repository/Testの見直し
2 parents e09f8fb + b99d43b commit 423b63a

24 files changed

Lines changed: 380 additions & 243 deletions

File tree

AndroidApp/app/src/main/kotlin/me/nya_n/notificationnotifier/App.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package me.nya_n.notificationnotifier
22

33
import android.app.Application
44
import me.nya_n.notificationnotifier.data.repository.AppRepository
5+
import me.nya_n.notificationnotifier.data.repository.BackupRepository
56
import me.nya_n.notificationnotifier.data.repository.UserSettingsRepository
67
import me.nya_n.notificationnotifier.data.repository.impl.AppRepositoryImpl
8+
import me.nya_n.notificationnotifier.data.repository.impl.BackupRepositoryImpl
79
import me.nya_n.notificationnotifier.data.repository.impl.UserSettingsRepositoryImpl
810
import me.nya_n.notificationnotifier.data.repository.source.DB
911
import me.nya_n.notificationnotifier.data.repository.source.UserSettingsDataStore
@@ -81,20 +83,21 @@ class App : Application() {
8183

8284
// Repository
8385
single<UserSettingsRepository> { UserSettingsRepositoryImpl(get()) }
84-
single<AppRepository> { AppRepositoryImpl(get(), get()) }
86+
single<AppRepository> { AppRepositoryImpl(applicationContext.packageManager, get(), get()) }
87+
single<BackupRepository> { BackupRepositoryImpl(applicationContext) }
8588

8689
// ViewModel
8790
viewModel { AppViewModel(get(), packageName, get(), get()) }
88-
viewModel { SelectionViewModel(get(), get(), get()) }
91+
viewModel { SelectionViewModel(get(), get()) }
8992
viewModel { params -> DetailViewModel(get(), get(), get(), get(), params.get()) }
90-
viewModel { TargetViewModel(get(), get()) }
93+
viewModel { TargetViewModel(get()) }
9194
viewModel { SettingsViewModel(get(), get(), get(), get(), get(), get()) }
9295

9396
// UseCase
9497
factory<AddTargetAppUseCase> { AddTargetAppUseCaseImpl(get()) }
9598
factory<DeleteTargetAppUseCase> { DeleteTargetAppUseCaseImpl(get()) }
96-
factory<ExportDataUseCase> { ExportDataUseCaseImpl(get(), get()) }
97-
factory<ImportDataUseCase> { ImportDataUseCaseImpl(get(), get()) }
99+
factory<ExportDataUseCase> { ExportDataUseCaseImpl(get(), get(), get()) }
100+
factory<ImportDataUseCase> { ImportDataUseCaseImpl(get(), get(), get()) }
98101
factory<LoadAddressUseCase> { LoadAddressUseCaseImpl(get()) }
99102
factory<LoadAppUseCase> { LoadAppUseCaseImpl(get(), get()) }
100103
factory<LoadFilterConditionUseCase> { LoadFilterConditionUseCaseImpl(get()) }

AndroidApp/data/repository/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ dependencies {
2020
// room
2121
api(libs.androidx.room.runtime)
2222
ksp(libs.androidx.room.compiler)
23+
24+
// test
25+
androidTestImplementation(libs.junit)
26+
androidTestImplementation(libs.com.google.truth)
27+
androidTestImplementation(libs.androidx.test.ext.junit)
28+
androidTestImplementation(libs.kotlinx.coroutines.test)
2329
}
2430

2531
ksp {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package me.nya_n.notificationnotifier
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import com.google.common.truth.Truth.assertThat
6+
import kotlinx.coroutines.ExperimentalCoroutinesApi
7+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
8+
import kotlinx.coroutines.test.runTest
9+
import me.nya_n.notificationnotifier.data.repository.AppRepository
10+
import me.nya_n.notificationnotifier.data.repository.impl.AppRepositoryImpl
11+
import me.nya_n.notificationnotifier.data.repository.source.DB
12+
import me.nya_n.notificationnotifier.model.FilterCondition
13+
import me.nya_n.notificationnotifier.model.InstalledApp
14+
import org.junit.Before
15+
import org.junit.Test
16+
import org.junit.runner.RunWith
17+
18+
@Suppress("NonAsciiCharacters")
19+
@RunWith(AndroidJUnit4::class)
20+
class AppRepositoryTest {
21+
@OptIn(ExperimentalCoroutinesApi::class)
22+
private val testDispatcher = UnconfinedTestDispatcher()
23+
private lateinit var appRepository: AppRepository
24+
25+
@Before
26+
fun setUp() {
27+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
28+
val db = DB.get(appContext, isInMemory = true).apply {
29+
clearAllTables()
30+
}
31+
appRepository = AppRepositoryImpl(
32+
appContext.packageManager,
33+
db.filterConditionDao(),
34+
db.targetAppDao(),
35+
testDispatcher
36+
)
37+
}
38+
39+
@Test
40+
fun `通知対象アプリの追加、取得、削除`() {
41+
runTest(testDispatcher) {
42+
val app = InstalledApp("sample", "com.sample.www")
43+
appRepository.addTargetApp(app)
44+
45+
val added = appRepository.getTargetAppList()
46+
assertThat(added).hasSize(1)
47+
assertThat(added.first()).isEqualTo(app)
48+
49+
appRepository.deleteTargetApp(app)
50+
val deleted = appRepository.getTargetAppList()
51+
assertThat(deleted).isEmpty()
52+
}
53+
}
54+
55+
56+
@Test
57+
fun `通知条件の追加、取得、更新`() {
58+
runTest(testDispatcher) {
59+
val packageName = "com.sample.www"
60+
61+
// データなし
62+
assertThat(appRepository.getFilterCondition(packageName)).isNull()
63+
assertThat(appRepository.getFilterConditionOrDefault(packageName))
64+
.isEqualTo(FilterCondition.default(packageName))
65+
66+
// 追加
67+
val added = FilterCondition(packageName, false, "test")
68+
appRepository.saveFilterCondition(added)
69+
assertThat(appRepository.getFilterCondition(packageName)).isEqualTo(added)
70+
71+
// メッセージ条件の更新
72+
val updatedCondition = added.copy(condition = "updated")
73+
appRepository.saveFilterCondition(updatedCondition)
74+
assertThat(appRepository.getFilterCondition(packageName)).isEqualTo(updatedCondition)
75+
76+
// サマリー条件の更新
77+
val updatedSummary = updatedCondition.copy(isIgnoreSummary = true)
78+
appRepository.saveFilterCondition(updatedSummary)
79+
assertThat(appRepository.getFilterCondition(packageName)).isEqualTo(updatedSummary)
80+
}
81+
}
82+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package me.nya_n.notificationnotifier
2+
3+
import androidx.core.content.edit
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import androidx.test.platform.app.InstrumentationRegistry
6+
import com.google.common.truth.Truth.assertThat
7+
import kotlinx.coroutines.ExperimentalCoroutinesApi
8+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
9+
import kotlinx.coroutines.test.runTest
10+
import me.nya_n.notificationnotifier.data.repository.UserSettingsRepository
11+
import me.nya_n.notificationnotifier.data.repository.impl.UserSettingsRepositoryImpl
12+
import me.nya_n.notificationnotifier.data.repository.source.UserSettingsDataStore
13+
import me.nya_n.notificationnotifier.data.repository.util.SharedPreferenceProvider
14+
import me.nya_n.notificationnotifier.model.UserSettings
15+
import org.junit.Before
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
19+
@Suppress("NonAsciiCharacters")
20+
@RunWith(AndroidJUnit4::class)
21+
class UserSettingsRepositoryTest {
22+
@OptIn(ExperimentalCoroutinesApi::class)
23+
private val testDispatcher = UnconfinedTestDispatcher()
24+
private lateinit var userSettingsRepository: UserSettingsRepository
25+
26+
@Before
27+
fun setUp() {
28+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
29+
userSettingsRepository = UserSettingsRepositoryImpl(
30+
UserSettingsDataStore(
31+
SharedPreferenceProvider.create(
32+
appContext,
33+
UserSettingsDataStore.DATA_STORE_NAME
34+
).apply {
35+
edit {
36+
clear()
37+
}
38+
}
39+
)
40+
)
41+
}
42+
43+
@Test
44+
fun `ユーザー設定の保存、取得、更新`() {
45+
runTest(testDispatcher) {
46+
val data = UserSettings("192.168.10.18", 8484, false)
47+
userSettingsRepository.saveUserSettings(data)
48+
assertThat(userSettingsRepository.getUserSettings()).isEqualTo(data)
49+
50+
val updated = data.copy(port = 2525, isPackageVisibilityGranted = true)
51+
userSettingsRepository.saveUserSettings(updated)
52+
assertThat(userSettingsRepository.getUserSettings()).isEqualTo(updated)
53+
}
54+
}
55+
}

AndroidApp/data/repository/src/main/kotlin/me/nya_n/notificationnotifier/data/repository/AppRepository.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package me.nya_n.notificationnotifier.data.repository
22

3-
import android.content.pm.PackageManager
43
import me.nya_n.notificationnotifier.model.FilterCondition
54
import me.nya_n.notificationnotifier.model.InstalledApp
65

@@ -23,5 +22,5 @@ interface AppRepository {
2322

2423
suspend fun deleteTargetApp(target: InstalledApp)
2524

26-
fun loadInstalledAppList(pm: PackageManager): List<InstalledApp>
25+
fun loadInstalledAppList(): List<InstalledApp>
2726
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package me.nya_n.notificationnotifier.data.repository
2+
3+
import android.net.Uri
4+
5+
interface BackupRepository {
6+
/** [uri]に通知対象や条件、設定を保存
7+
* @param uri 保存先
8+
* @param data 保存するデータ
9+
*/
10+
suspend fun exportToUri(uri: Uri, data: String)
11+
12+
/** [uri]から通知対象や条件、設定を復元
13+
* @param uri 読み込み元
14+
* @return 復元したデータ
15+
*/
16+
suspend fun importFromUri(uri: Uri): String
17+
}

AndroidApp/data/repository/src/main/kotlin/me/nya_n/notificationnotifier/data/repository/impl/AppRepositoryImpl.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import me.nya_n.notificationnotifier.model.FilterCondition
1111
import me.nya_n.notificationnotifier.model.InstalledApp
1212

1313
class AppRepositoryImpl(
14+
private val packageManager: PackageManager,
1415
private val filterConditionDao: FilterConditionDao,
1516
private val targetAppDao: TargetAppDao,
1617
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
@@ -62,10 +63,10 @@ class AppRepositoryImpl(
6263
}
6364
}
6465

65-
override fun loadInstalledAppList(pm: PackageManager): List<InstalledApp> {
66-
return pm.getInstalledApplications(PackageManager.GET_META_DATA)
66+
override fun loadInstalledAppList(): List<InstalledApp> {
67+
return packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
6768
.map {
68-
val label = pm.getApplicationLabel(it).toString()
69+
val label = packageManager.getApplicationLabel(it).toString()
6970
InstalledApp(
7071
label,
7172
it.packageName
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package me.nya_n.notificationnotifier.data.repository.impl
2+
3+
import android.content.Context
4+
import android.net.Uri
5+
import kotlinx.coroutines.CoroutineDispatcher
6+
import kotlinx.coroutines.Dispatchers
7+
import kotlinx.coroutines.withContext
8+
import me.nya_n.notificationnotifier.data.repository.BackupRepository
9+
import java.io.BufferedReader
10+
import java.io.InputStreamReader
11+
12+
class BackupRepositoryImpl(
13+
private val context: Context,
14+
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
15+
) : BackupRepository {
16+
override suspend fun exportToUri(uri: Uri, data: String) {
17+
withContext(coroutineDispatcher) {
18+
context.contentResolver.openOutputStream(uri)?.use {
19+
it.write(data.toByteArray())
20+
} ?: throw RuntimeException("Failed to open output stream.")
21+
}
22+
}
23+
24+
override suspend fun importFromUri(uri: Uri): String {
25+
val sb = StringBuilder()
26+
withContext(coroutineDispatcher) {
27+
context.contentResolver.openInputStream(uri)?.use { input ->
28+
BufferedReader(InputStreamReader(input)).use { reader ->
29+
sb.append(reader.readLine())
30+
} ?: throw RuntimeException("Failed to read input stream.")
31+
} ?: throw RuntimeException("Failed to open input stream.")
32+
}
33+
return sb.toString()
34+
}
35+
}

AndroidApp/domain/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ dependencies {
2121
api(libs.androidx.compose.runtime)
2222

2323
// test
24+
testImplementation(libs.junit)
25+
testImplementation(libs.com.google.truth)
26+
testImplementation(libs.kotlinx.coroutines.test)
27+
testImplementation(libs.mockk)
2428
androidTestImplementation(libs.junit)
2529
androidTestImplementation(libs.com.google.truth)
2630
androidTestImplementation(libs.androidx.test.ext.junit)
2731
androidTestImplementation(libs.androidx.test.espresso.core)
2832
androidTestImplementation(libs.kotlinx.coroutines.test)
33+
androidTestImplementation(libs.mockk)
34+
androidTestImplementation(libs.mockk.android)
2935

3036
// その他
3137
implementation(libs.com.google.code.gson)

0 commit comments

Comments
 (0)