Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.example.android.architecture.blueprints.todoapp.data.source

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.map
import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.runBlocking

class FakeAndroidDefaultTaskRepository : IDefaultTasksRepository {

var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()

private var shouldReturnError = false

private val observableTasks = MutableLiveData<Result<List<Task>>>()

fun setReturnError(value: Boolean) {
shouldReturnError = value
}

override suspend fun refreshTasks() {
observableTasks.value = getTasks()
}

override suspend fun refreshTask(taskId: String) {
refreshTasks()
}

override suspend fun updateTasksFromRemoteDataSource() {
TODO("Not yet implemented")
}

override fun observeTasks(): LiveData<Result<List<Task>>> {
runBlocking { refreshTasks() }
return observableTasks
}

override fun observeTask(taskId: String): LiveData<Result<Task>> {
runBlocking { refreshTasks() }
return observableTasks.map { tasks ->
when (tasks) {
is Result.Loading -> Result.Loading
is Result.Error -> Result.Error(tasks.exception)
is Result.Success -> {
val task = tasks.data.firstOrNull { it.id == taskId }
?: return@map Result.Error(Exception("Not found"))
Result.Success(task)
}
}
}
}

override suspend fun updateTaskFromRemoteDataSource(taskId: String) {
TODO("Not yet implemented")
}

override suspend fun getTask(taskId: String, forceUpdate: Boolean): Result<Task> {
if (shouldReturnError) {
return Result.Error(Exception("Test exception"))
}
tasksServiceData[taskId]?.let {
return Result.Success(it)
}
return Result.Error(Exception("Could not find task"))
}

override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
if (shouldReturnError) {
return Result.Error(Exception("Test exception"))
}
return Result.Success(tasksServiceData.values.toList())
}

override suspend fun saveTask(task: Task) {
tasksServiceData[task.id] = task
}

override suspend fun completeTask(task: Task) {
val completedTask = Task(task.title, task.description, true, task.id)
tasksServiceData[task.id] = completedTask
}

override suspend fun completeTask(taskId: String) {
// Not required for the remote data source.
throw NotImplementedError()
}

override suspend fun activateTask(task: Task) {
val activeTask = Task(task.title, task.description, false, task.id)
tasksServiceData[task.id] = activeTask
}

override suspend fun activateTask(taskId: String) {
throw NotImplementedError()
}

override suspend fun clearCompletedTasks() {
tasksServiceData = tasksServiceData.filterValues {
!it.isCompleted
} as LinkedHashMap<String, Task>
}

override suspend fun deleteTask(taskId: String) {
tasksServiceData.remove(taskId)
refreshTasks()
}

override suspend fun getTaskWithId(id: String): Result<Task> {
TODO("Not yet implemented")
}

override suspend fun deleteAllTasks() {
tasksServiceData.clear()
refreshTasks()
}


fun addTasks(vararg tasks: Task) {
for (task in tasks) {
tasksServiceData[task.id] = task
}
runBlocking { refreshTasks() }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,41 @@ import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.example.android.architecture.blueprints.todoapp.R
import com.example.android.architecture.blueprints.todoapp.ServiceLocator
import com.example.android.architecture.blueprints.todoapp.data.Task
import com.example.android.architecture.blueprints.todoapp.data.source.FakeAndroidDefaultTaskRepository
import com.example.android.architecture.blueprints.todoapp.data.source.IDefaultTasksRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@ExperimentalCoroutinesApi
@MediumTest
@RunWith(AndroidJUnit4::class)
internal class TaskDetailFragmentTest {

private lateinit var repository: IDefaultTasksRepository

@Before
fun initRepository() {
repository = FakeAndroidDefaultTaskRepository()
ServiceLocator.tasksRepository = repository
}

@After
fun cleanupDb() = runBlockingTest {
ServiceLocator.resetRepository()
}

@Test
fun activeTaskDetails_DisplayedInUi() {
fun activeTaskDetails_DisplayedInUi() = runBlockingTest {
// Assign - Add active (incomplete) task to the DB
val activeTask = Task("Active Task", "AndroidX Rocks", false)

repository.saveTask(activeTask)

// Act - Details fragment launched to display task
val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
package com.example.android.architecture.blueprints.todoapp

import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.room.Room
import com.example.android.architecture.blueprints.todoapp.data.source.DefaultTasksRepository
import com.example.android.architecture.blueprints.todoapp.data.source.IDefaultTasksRepository
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource
import com.example.android.architecture.blueprints.todoapp.data.source.local.TasksLocalDataSource
import com.example.android.architecture.blueprints.todoapp.data.source.local.ToDoDatabase
import com.example.android.architecture.blueprints.todoapp.data.source.remote.TasksRemoteDataSource
import kotlinx.coroutines.runBlocking

object ServiceLocator {

private val lock = Any()
private var database: ToDoDatabase? = null

@Volatile
private var tasksRepository: IDefaultTasksRepository? = null
var tasksRepository: IDefaultTasksRepository? = null
@VisibleForTesting set

@VisibleForTesting
fun resetRepository() {
synchronized(lock) {
runBlocking {
TasksRemoteDataSource.deleteAllTasks()
}
// clear all data to avoid test pollution
database?.apply {
clearAllTables()
close()
}
database = null
tasksRepository = null
}
}

fun provideTaskRepository(context: Context): IDefaultTasksRepository {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.example.android.architecture.blueprints.todoapp.EventObserver
import com.example.android.architecture.blueprints.todoapp.R
import com.example.android.architecture.blueprints.todoapp.TodoApplication
import com.example.android.architecture.blueprints.todoapp.data.source.DefaultTasksRepository
import com.example.android.architecture.blueprints.todoapp.data.source.IDefaultTasksRepository
import com.example.android.architecture.blueprints.todoapp.databinding.TaskdetailFragBinding
import com.example.android.architecture.blueprints.todoapp.tasks.DELETE_RESULT_OK
import com.example.android.architecture.blueprints.todoapp.util.setupRefreshLayout
Expand All @@ -36,6 +37,7 @@ import com.google.android.material.snackbar.Snackbar
*/
class TaskDetailFragment : Fragment() {
private lateinit var viewDataBinding: TaskdetailFragBinding
private lateinit var repository: IDefaultTasksRepository

private val args: TaskDetailFragmentArgs by navArgs()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class FakeDefaultTasksRepository : IDefaultTasksRepository {
TODO("Not yet implemented")
}

override suspend fun saveTask(task: Task) {
TODO("Not yet implemented")
}

fun addTask(vararg tasks: Task) {
tasks.forEach { task ->
tasksData[task.id] = task
Expand Down