Skip to content

Commit 8d09ea7

Browse files
committed
테스트 - ServiceLocator 적용하여 Fragment 계측 테스트 실행
1. ServiceLocator 적용하여 Fragment 계측 테스트 실행
1 parent 5d4d7b4 commit 8d09ea7

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailFragmentTest.kt

+22-1
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,41 @@ import androidx.fragment.app.testing.launchFragmentInContainer
44
import androidx.test.ext.junit.runners.AndroidJUnit4
55
import androidx.test.filters.MediumTest
66
import com.example.android.architecture.blueprints.todoapp.R
7+
import com.example.android.architecture.blueprints.todoapp.ServiceLocator
78
import com.example.android.architecture.blueprints.todoapp.data.Task
9+
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
10+
import com.example.android.architecture.blueprints.todoapp.data.source.androidTest.FakeAndroidTestRepository
11+
import kotlinx.coroutines.ExperimentalCoroutinesApi
12+
import kotlinx.coroutines.test.runBlockingTest
13+
import org.junit.After
814
import org.junit.Assert.*
15+
import org.junit.Before
916
import org.junit.Test
1017
import org.junit.runner.RunWith
1118

19+
@ExperimentalCoroutinesApi
1220
@MediumTest
1321
@RunWith(AndroidJUnit4::class)
1422
class TaskDetailFragmentTest {
1523

24+
private lateinit var repository: TasksRepository
25+
26+
@Before
27+
fun initRepository() {
28+
repository = FakeAndroidTestRepository()
29+
ServiceLocator.tasksRepository = repository
30+
}
31+
32+
@After
33+
fun cleanupDb() = runBlockingTest {
34+
ServiceLocator.resetRepository()
35+
}
1636

1737
@Test
18-
fun activeTaskDetails_DisplayedInUi() {
38+
fun activeTaskDetails_DisplayedInUi() = runBlockingTest {
1939
// GIVEN - Add active (incomplete) task to the DB
2040
val activeTask = Task("Active Task", "AndroidX Rocks", false)
41+
repository.saveTask(activeTask)
2142

2243
// WHEN - Details fragment launched to display task
2344
val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()

app/src/main/java/com/example/android/architecture/blueprints/todoapp/ServiceLocator.kt

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.android.architecture.blueprints.todoapp
22

33
import android.content.Context
4+
import androidx.annotation.VisibleForTesting
45
import androidx.room.Room
56
import com.example.android.architecture.blueprints.todoapp.data.source.DefaultTasksRepository
67
import com.example.android.architecture.blueprints.todoapp.data.source.FakeTestRepository
@@ -9,13 +10,33 @@ import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepo
910
import com.example.android.architecture.blueprints.todoapp.data.source.local.TasksLocalDataSource
1011
import com.example.android.architecture.blueprints.todoapp.data.source.local.ToDoDatabase
1112
import com.example.android.architecture.blueprints.todoapp.data.source.remote.TasksRemoteDataSource
13+
import kotlinx.coroutines.runBlocking
1214

1315
object ServiceLocator {
1416

1517
private var database: ToDoDatabase? = null
1618

1719
@Volatile
1820
var tasksRepository: TasksRepository? = null
21+
@VisibleForTesting set
22+
23+
private val lock = Any()
24+
25+
@VisibleForTesting
26+
fun resetRepository() {
27+
synchronized(lock) {
28+
runBlocking {
29+
TasksRemoteDataSource.deleteAllTasks()
30+
}
31+
// Clear all data to avoid test pollution.
32+
database?.apply {
33+
clearAllTables()
34+
close()
35+
}
36+
database = null
37+
tasksRepository = null
38+
}
39+
}
1940

2041
fun provideTasksRepository(context: Context): TasksRepository {
2142
synchronized(this) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.example.android.architecture.blueprints.todoapp.data.source.androidTest
2+
3+
import androidx.lifecycle.LiveData
4+
import com.example.android.architecture.blueprints.todoapp.data.*
5+
import androidx.lifecycle.MutableLiveData
6+
import androidx.lifecycle.map
7+
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
8+
import kotlinx.coroutines.runBlocking
9+
10+
11+
class FakeAndroidTestRepository: TasksRepository {
12+
13+
var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()
14+
15+
private var shouldReturnError = false
16+
17+
private val observableTasks = MutableLiveData<Result<List<Task>>>()
18+
19+
fun setReturnError(value: Boolean) {
20+
shouldReturnError = value
21+
}
22+
23+
override suspend fun refreshTasks() {
24+
observableTasks.value = getTasks() as Result<List<Task>>
25+
}
26+
27+
override suspend fun refreshTask(taskId: String) {
28+
refreshTasks()
29+
}
30+
31+
override fun observeTasks(): LiveData<Result<List<Task>>> {
32+
runBlocking { refreshTasks() }
33+
return observableTasks
34+
}
35+
36+
override fun observeTask(taskId: String): LiveData<Result<Task>> {
37+
runBlocking { refreshTasks() }
38+
return observableTasks.map { tasks ->
39+
when (tasks) {
40+
is Result.Loading -> Result.Loading
41+
is Result.Error -> Result.Error(tasks.exception)
42+
is Result.Success -> {
43+
val task = tasks.data.firstOrNull() { it.id == taskId }
44+
?: return@map Result.Error(Exception("Not found"))
45+
Result.Success(task)
46+
}
47+
}
48+
}
49+
}
50+
51+
override suspend fun getTask(taskId: String, forceUpdate: Boolean): Result<Task> {
52+
if (shouldReturnError) {
53+
return Result.Error(Exception("Test exception"))
54+
}
55+
tasksServiceData[taskId]?.let {
56+
return Result.Success(it)
57+
}
58+
return Result.Error(Exception("Could not find task"))
59+
}
60+
61+
override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
62+
if (shouldReturnError) {
63+
return Result.Error(Exception("Test exception"))
64+
}
65+
return Result.Success(tasksServiceData.values.toList())
66+
}
67+
68+
override suspend fun saveTask(task: Task) {
69+
tasksServiceData[task.id] = task
70+
}
71+
72+
override suspend fun completeTask(task: Task) {
73+
val completedTask = Task(task.title, task.description, true, task.id)
74+
tasksServiceData[task.id] = completedTask
75+
}
76+
77+
override suspend fun completeTask(taskId: String) {
78+
// Not required for the remote data source.
79+
throw NotImplementedError()
80+
}
81+
82+
override suspend fun activateTask(task: Task) {
83+
val activeTask = Task(task.title, task.description, false, task.id)
84+
tasksServiceData[task.id] = activeTask
85+
}
86+
87+
override suspend fun activateTask(taskId: String) {
88+
throw NotImplementedError()
89+
}
90+
91+
override suspend fun clearCompletedTasks() {
92+
tasksServiceData = tasksServiceData.filterValues {
93+
!it.isCompleted
94+
} as LinkedHashMap<String, Task>
95+
}
96+
97+
override suspend fun deleteTask(taskId: String) {
98+
tasksServiceData.remove(taskId)
99+
refreshTasks()
100+
}
101+
102+
override suspend fun deleteAllTasks() {
103+
tasksServiceData.clear()
104+
refreshTasks()
105+
}
106+
107+
108+
fun addTasks(vararg tasks: Task) {
109+
for (task in tasks) {
110+
tasksServiceData[task.id] = task
111+
}
112+
runBlocking { refreshTasks() }
113+
}
114+
}

0 commit comments

Comments
 (0)