diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskScreenTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskScreenTest.kt index f7b68a521..4ff93214e 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskScreenTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskScreenTest.kt @@ -106,7 +106,7 @@ class AddEditTaskScreenTest { .performClick() // THEN - Verify that the repository saved the task - val tasks = repository.getTasks(true) + val tasks = repository.getAll(true) assertEquals(1, tasks.size) assertEquals("title", tasks[0].title) assertEquals("description", tasks[0].description) diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsScreenTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsScreenTest.kt index ac28599c4..685ee2e0f 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsScreenTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsScreenTest.kt @@ -64,9 +64,9 @@ class StatisticsScreenTest { fun tasks_showsNonEmptyMessage() = runTest { // Given some tasks repository.apply { - createTask("Title1", "Description1") - createTask("Title2", "Description2").also { - completeTask(it) + create("Title1", "Description1") + create("Title2", "Description2").also { + complete(it) } } diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailScreenTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailScreenTest.kt index 666f76747..0553dd686 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailScreenTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailScreenTest.kt @@ -65,7 +65,7 @@ class TaskDetailScreenTest { @Test fun activeTaskDetails_DisplayedInUi() = runTest { // GIVEN - Add active (incomplete) task to the DB - val activeTaskId = repository.createTask( + val activeTaskId = repository.create( title = "Active Task", description = "AndroidX Rocks" ) @@ -84,8 +84,8 @@ class TaskDetailScreenTest { @Test fun completedTaskDetails_DisplayedInUi() = runTest { // GIVEN - Add completed task to the DB - val completedTaskId = repository.createTask("Completed Task", "AndroidX Rocks") - repository.completeTask(completedTaskId) + val completedTaskId = repository.create("Completed Task", "AndroidX Rocks") + repository.complete(completedTaskId) // WHEN - Details screen is opened setContent(completedTaskId) diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.kt index 2d9014eb9..3b45c0a8e 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.kt @@ -130,7 +130,7 @@ class AppNavigationTest { @Test fun taskDetailScreen_doubleUIBackButton() = runTest { val taskName = "UI <- button" - taskRepository.createTask(taskName, "Description") + taskRepository.create(taskName, "Description") setContent() @@ -159,7 +159,7 @@ class AppNavigationTest { @Test fun taskDetailScreen_doubleBackButton() = runTest { val taskName = "Back button" - taskRepository.createTask(taskName, "Description") + taskRepository.create(taskName, "Description") setContent() diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.kt index a91ad574c..ff3cc6d6b 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.kt @@ -71,7 +71,7 @@ class TasksScreenTest { @Test fun displayTask_whenRepositoryHasData() = runTest { // GIVEN - One task already in the repository - repository.createTask("TITLE1", "DESCRIPTION1") + repository.create("TITLE1", "DESCRIPTION1") // WHEN - On startup setContent() @@ -82,7 +82,7 @@ class TasksScreenTest { @Test fun displayActiveTask() = runTest { - repository.createTask("TITLE1", "DESCRIPTION1") + repository.create("TITLE1", "DESCRIPTION1") setContent() @@ -99,7 +99,7 @@ class TasksScreenTest { @Test fun displayCompletedTask() = runTest { repository.apply { - createTask("TITLE1", "DESCRIPTION1").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1").also { complete(it) } } setContent() @@ -115,7 +115,7 @@ class TasksScreenTest { @Test fun markTaskAsComplete() = runTest { - repository.createTask("TITLE1", "DESCRIPTION1") + repository.create("TITLE1", "DESCRIPTION1") setContent() @@ -134,7 +134,7 @@ class TasksScreenTest { @Test fun markTaskAsActive() = runTest { repository.apply { - createTask("TITLE1", "DESCRIPTION1").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1").also { complete(it) } } setContent() @@ -155,8 +155,8 @@ class TasksScreenTest { fun showAllTasks() = runTest { // Add one active task and one completed task repository.apply { - createTask("TITLE1", "DESCRIPTION1") - createTask("TITLE2", "DESCRIPTION2").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1") + create("TITLE2", "DESCRIPTION2").also { complete(it) } } setContent() @@ -171,9 +171,9 @@ class TasksScreenTest { fun showActiveTasks() = runTest { // Add 2 active tasks and one completed task repository.apply { - createTask("TITLE1", "DESCRIPTION1") - createTask("TITLE2", "DESCRIPTION2") - createTask("TITLE3", "DESCRIPTION3").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1") + create("TITLE2", "DESCRIPTION2") + create("TITLE3", "DESCRIPTION3").also { complete(it) } } setContent() @@ -189,9 +189,9 @@ class TasksScreenTest { fun showCompletedTasks() = runTest { // Add one active task and 2 completed tasks repository.apply { - createTask("TITLE1", "DESCRIPTION1") - createTask("TITLE2", "DESCRIPTION2").also { completeTask(it) } - createTask("TITLE3", "DESCRIPTION3").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1") + create("TITLE2", "DESCRIPTION2").also { complete(it) } + create("TITLE3", "DESCRIPTION3").also { complete(it) } } setContent() @@ -207,8 +207,8 @@ class TasksScreenTest { fun clearCompletedTasks() = runTest { // Add one active task and one completed task repository.apply { - createTask("TITLE1", "DESCRIPTION1") - createTask("TITLE2", "DESCRIPTION2").also { completeTask(it) } + create("TITLE1", "DESCRIPTION1") + create("TITLE2", "DESCRIPTION2").also { complete(it) } } setContent() diff --git a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksTest.kt b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksTest.kt index 8d3097e87..06d5c56c3 100644 --- a/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksTest.kt +++ b/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksTest.kt @@ -78,7 +78,7 @@ class TasksTest { @Test fun editTask() = runTest { val originalTaskTitle = "TITLE1" - repository.createTask(originalTaskTitle, "DESCRIPTION") + repository.create(originalTaskTitle, "DESCRIPTION") setContent() @@ -143,8 +143,8 @@ class TasksTest { @Test fun createTwoTasks_deleteOneTask() = runTest { repository.apply { - createTask("TITLE1", "DESCRIPTION") - createTask("TITLE2", "DESCRIPTION") + create("TITLE1", "DESCRIPTION") + create("TITLE2", "DESCRIPTION") } setContent() @@ -169,7 +169,7 @@ class TasksTest { fun markTaskAsCompleteOnDetailScreen_taskIsCompleteInList() = runTest { // Add 1 active task val taskTitle = "COMPLETED" - repository.createTask(taskTitle, "DESCRIPTION") + repository.create(taskTitle, "DESCRIPTION") setContent() @@ -195,7 +195,7 @@ class TasksTest { // Add 1 completed task val taskTitle = "ACTIVE" repository.apply { - createTask(taskTitle, "DESCRIPTION").also { completeTask(it) } + create(taskTitle, "DESCRIPTION").also { complete(it) } } setContent() @@ -221,7 +221,7 @@ class TasksTest { fun markTaskAsCompleteAndActiveOnDetailScreen_taskIsActiveInList() = runTest { // Add 1 active task val taskTitle = "ACT-COMP" - repository.createTask(taskTitle, "DESCRIPTION") + repository.create(taskTitle, "DESCRIPTION") setContent() @@ -249,7 +249,7 @@ class TasksTest { // Add 1 completed task val taskTitle = "COMP-ACT" repository.apply { - createTask(taskTitle, "DESCRIPTION").also { completeTask(it) } + create(taskTitle, "DESCRIPTION").also { complete(it) } } setContent() diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskViewModel.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskViewModel.kt index 247573042..b9d8fa957 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskViewModel.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskViewModel.kt @@ -100,7 +100,7 @@ class AddEditTaskViewModel @Inject constructor( } private fun createNewTask() = viewModelScope.launch { - taskRepository.createTask(uiState.value.title, uiState.value.description) + taskRepository.create(uiState.value.title, uiState.value.description) _uiState.update { it.copy(isTaskSaved = true) } @@ -111,7 +111,7 @@ class AddEditTaskViewModel @Inject constructor( throw RuntimeException("updateTask() was called but task is new.") } viewModelScope.launch { - taskRepository.updateTask( + taskRepository.update( taskId, title = uiState.value.title, description = uiState.value.description, @@ -127,7 +127,7 @@ class AddEditTaskViewModel @Inject constructor( it.copy(isLoading = true) } viewModelScope.launch { - taskRepository.getTask(taskId).let { task -> + taskRepository.get(taskId).let { task -> if (task != null) { _uiState.update { it.copy( diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepository.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepository.kt index 9fa32de87..65637f156 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepository.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepository.kt @@ -48,7 +48,7 @@ class DefaultTaskRepository @Inject constructor( @ApplicationScope private val scope: CoroutineScope, ) : TaskRepository { - override suspend fun createTask(title: String, description: String): String { + override suspend fun create(title: String, description: String): String { // ID creation might be a complex operation so it's executed using the supplied // coroutine dispatcher val taskId = withContext(dispatcher) { @@ -64,8 +64,8 @@ class DefaultTaskRepository @Inject constructor( return taskId } - override suspend fun updateTask(taskId: String, title: String, description: String) { - val task = getTask(taskId)?.copy( + override suspend fun update(taskId: String, title: String, description: String) { + val task = get(taskId)?.copy( title = title, description = description ) ?: throw Exception("Task (id $taskId) not found") @@ -74,7 +74,7 @@ class DefaultTaskRepository @Inject constructor( saveTasksToNetwork() } - override suspend fun getTasks(forceUpdate: Boolean): List { + override suspend fun getAll(forceUpdate: Boolean): List { if (forceUpdate) { refresh() } @@ -83,7 +83,7 @@ class DefaultTaskRepository @Inject constructor( } } - override fun getTasksStream(): Flow> { + override fun observeAll(): Flow> { return localDataSource.observeAll().map { tasks -> withContext(dispatcher) { tasks.toExternal() @@ -91,11 +91,11 @@ class DefaultTaskRepository @Inject constructor( } } - override suspend fun refreshTask(taskId: String) { + override suspend fun refresh(taskId: String) { refresh() } - override fun getTaskStream(taskId: String): Flow { + override fun observe(taskId: String): Flow { return localDataSource.observeById(taskId).map { it.toExternal() } } @@ -105,34 +105,34 @@ class DefaultTaskRepository @Inject constructor( * @param taskId - The ID of the task * @param forceUpdate - true if the task should be updated from the network data source first. */ - override suspend fun getTask(taskId: String, forceUpdate: Boolean): Task? { + override suspend fun get(taskId: String, forceUpdate: Boolean): Task? { if (forceUpdate) { refresh() } return localDataSource.getById(taskId)?.toExternal() } - override suspend fun completeTask(taskId: String) { + override suspend fun complete(taskId: String) { localDataSource.updateCompleted(taskId = taskId, completed = true) saveTasksToNetwork() } - override suspend fun activateTask(taskId: String) { + override suspend fun activate(taskId: String) { localDataSource.updateCompleted(taskId = taskId, completed = false) saveTasksToNetwork() } - override suspend fun clearCompletedTasks() { + override suspend fun clearAllCompleted() { localDataSource.deleteCompleted() saveTasksToNetwork() } - override suspend fun deleteAllTasks() { + override suspend fun deleteAll() { localDataSource.deleteAll() saveTasksToNetwork() } - override suspend fun deleteTask(taskId: String) { + override suspend fun delete(taskId: String) { localDataSource.deleteById(taskId) saveTasksToNetwork() } diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/TaskRepository.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/TaskRepository.kt index 6f0ee0bb5..31fa69604 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/TaskRepository.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/TaskRepository.kt @@ -23,29 +23,29 @@ import kotlinx.coroutines.flow.Flow */ interface TaskRepository { - fun getTasksStream(): Flow> + fun observeAll(): Flow> - suspend fun getTasks(forceUpdate: Boolean = false): List + suspend fun getAll(forceUpdate: Boolean = false): List suspend fun refresh() - fun getTaskStream(taskId: String): Flow + fun observe(taskId: String): Flow - suspend fun getTask(taskId: String, forceUpdate: Boolean = false): Task? + suspend fun get(taskId: String, forceUpdate: Boolean = false): Task? - suspend fun refreshTask(taskId: String) + suspend fun refresh(taskId: String) - suspend fun createTask(title: String, description: String): String + suspend fun create(title: String, description: String): String - suspend fun updateTask(taskId: String, title: String, description: String) + suspend fun update(taskId: String, title: String, description: String) - suspend fun completeTask(taskId: String) + suspend fun complete(taskId: String) - suspend fun activateTask(taskId: String) + suspend fun activate(taskId: String) - suspend fun clearCompletedTasks() + suspend fun clearAllCompleted() - suspend fun deleteAllTasks() + suspend fun deleteAll() - suspend fun deleteTask(taskId: String) + suspend fun delete(taskId: String) } diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsViewModel.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsViewModel.kt index 4db5c1f22..c018b7e7c 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsViewModel.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsViewModel.kt @@ -50,7 +50,7 @@ class StatisticsViewModel @Inject constructor( ) : ViewModel() { val uiState: StateFlow = - taskRepository.getTasksStream() + taskRepository.observeAll() .map { Async.Success(it) } .catch>> { emit(Async.Error(R.string.loading_tasks_error)) } .map { taskAsync -> produceStatisticsUiState(taskAsync) } diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModel.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModel.kt index 080b22c04..f6e250571 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModel.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModel.kt @@ -59,7 +59,7 @@ class TaskDetailViewModel @Inject constructor( private val _userMessage: MutableStateFlow = MutableStateFlow(null) private val _isLoading = MutableStateFlow(false) private val _isTaskDeleted = MutableStateFlow(false) - private val _taskAsync = taskRepository.getTaskStream(taskId) + private val _taskAsync = taskRepository.observe(taskId) .map { handleTask(it) } .catch { emit(Async.Error(R.string.loading_task_error)) } @@ -93,17 +93,17 @@ class TaskDetailViewModel @Inject constructor( ) fun deleteTask() = viewModelScope.launch { - taskRepository.deleteTask(taskId) + taskRepository.delete(taskId) _isTaskDeleted.value = true } fun setCompleted(completed: Boolean) = viewModelScope.launch { val task = uiState.value.task ?: return@launch if (completed) { - taskRepository.completeTask(task.id) + taskRepository.complete(task.id) showSnackbarMessage(R.string.task_marked_complete) } else { - taskRepository.activateTask(task.id) + taskRepository.activate(task.id) showSnackbarMessage(R.string.task_marked_active) } } @@ -111,7 +111,7 @@ class TaskDetailViewModel @Inject constructor( fun refresh() { _isLoading.value = true viewModelScope.launch { - taskRepository.refreshTask(taskId) + taskRepository.refresh(taskId) _isLoading.value = false } } diff --git a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModel.kt b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModel.kt index 9868f80a7..91ba74f05 100644 --- a/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModel.kt +++ b/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModel.kt @@ -67,7 +67,7 @@ class TasksViewModel @Inject constructor( private val _userMessage: MutableStateFlow = MutableStateFlow(null) private val _isLoading = MutableStateFlow(false) private val _filteredTasksAsync = - combine(taskRepository.getTasksStream(), _savedFilterType) { tasks, type -> + combine(taskRepository.observeAll(), _savedFilterType) { tasks, type -> filterTasks(tasks, type) } .map { Async.Success(it) } @@ -105,7 +105,7 @@ class TasksViewModel @Inject constructor( fun clearCompletedTasks() { viewModelScope.launch { - taskRepository.clearCompletedTasks() + taskRepository.clearAllCompleted() showSnackbarMessage(R.string.completed_tasks_cleared) refresh() } @@ -113,10 +113,10 @@ class TasksViewModel @Inject constructor( fun completeTask(task: Task, completed: Boolean) = viewModelScope.launch { if (completed) { - taskRepository.completeTask(task.id) + taskRepository.complete(task.id) showSnackbarMessage(R.string.task_marked_complete) } else { - taskRepository.activateTask(task.id) + taskRepository.activate(task.id) showSnackbarMessage(R.string.task_marked_active) } } diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepositoryTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepositoryTest.kt index 1c41c8654..934ec4ae4 100644 --- a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepositoryTest.kt +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/data/DefaultTaskRepositoryTest.kt @@ -71,23 +71,23 @@ class DefaultTaskRepositoryTest { @ExperimentalCoroutinesApi @Test - fun getTasks_emptyRepositoryAndUninitializedCache() = testScope.runTest { + fun whenGettingAllTasks_emptyRepositoryAndUninitializedCache() = testScope.runTest { networkDataSource.tasks?.clear() localDataSource.deleteAll() - assertThat(taskRepository.getTasks().size).isEqualTo(0) + assertThat(taskRepository.getAll().size).isEqualTo(0) } @Test - fun getTasks_repositoryCachesAfterFirstApiCall() = testScope.runTest { + fun whenGettingAllTasks_repositoryCachesAfterFirstApiCall() = testScope.runTest { // Trigger the repository to load tasks from the remote data source - val initial = taskRepository.getTasks(forceUpdate = true) + val initial = taskRepository.getAll(forceUpdate = true) // Change the remote data source networkDataSource.tasks = newTasks.toNetwork().toMutableList() // Load the tasks again without forcing a refresh - val second = taskRepository.getTasks() + val second = taskRepository.getAll() // Initial and second should match because we didn't force a refresh (no tasks were loaded // from the remote data source) @@ -95,9 +95,9 @@ class DefaultTaskRepositoryTest { } @Test - fun getTasks_requestsAllTasksFromRemoteDataSource() = testScope.runTest { + fun whenGettingAllTasks_requestsAllTasksFromRemoteDataSource() = testScope.runTest { // When tasks are requested from the tasks repository - val tasks = taskRepository.getTasks(true) + val tasks = taskRepository.getAll(true) // Then tasks are loaded from the remote data source assertThat(tasks).isEqualTo(networkTasks.toExternal()) @@ -106,7 +106,7 @@ class DefaultTaskRepositoryTest { @Test fun saveTask_savesToLocalAndRemote() = testScope.runTest { // When a task is saved to the tasks repository - val newTaskId = taskRepository.createTask(newTask.title, newTask.description) + val newTaskId = taskRepository.create(newTask.title, newTask.description) // Then the remote and local sources contain the new task assertThat(networkDataSource.tasks?.map { it.id }?.contains(newTaskId)) @@ -114,61 +114,61 @@ class DefaultTaskRepositoryTest { } @Test - fun getTasks_WithDirtyCache_tasksAreRetrievedFromRemote() = testScope.runTest { + fun whenGettingAllTasks_WithDirtyCache_tasksAreRetrievedFromRemote() = testScope.runTest { // First call returns from REMOTE - val tasks = taskRepository.getTasks() + val tasks = taskRepository.getAll() // Set a different list of tasks in REMOTE networkDataSource.tasks = newTasks.toNetwork().toMutableList() // But if tasks are cached, subsequent calls load from cache - val cachedTasks = taskRepository.getTasks() + val cachedTasks = taskRepository.getAll() assertThat(cachedTasks).isEqualTo(tasks) // Now force remote loading - val refreshedTasks = taskRepository.getTasks(true) + val refreshedTasks = taskRepository.getAll(true) // Tasks must be the recently updated in REMOTE assertThat(refreshedTasks).isEqualTo(newTasks) } @Test(expected = Exception::class) - fun getTasks_WithDirtyCache_remoteUnavailable_throwsException() = testScope.runTest { + fun whenGettingAllTasks_WithDirtyCache_remoteUnavailable_throwsException() = testScope.runTest { // Make remote data source unavailable networkDataSource.tasks = null // Load tasks forcing remote load - taskRepository.getTasks(true) + taskRepository.getAll(true) // Exception should be thrown } @Test - fun getTasks_WithRemoteDataSourceUnavailable_tasksAreRetrievedFromLocal() = + fun whenGettingAllTasks_WithRemoteDataSourceUnavailable_tasksAreRetrievedFromLocal() = testScope.runTest { // When the remote data source is unavailable networkDataSource.tasks = null // The repository fetches from the local source - assertThat(taskRepository.getTasks()).isEqualTo(localTasks.toExternal()) + assertThat(taskRepository.getAll()).isEqualTo(localTasks.toExternal()) } @Test(expected = Exception::class) - fun getTasks_WithBothDataSourcesUnavailable_throwsError() = testScope.runTest { + fun whenGettingAllTasks_WithBothDataSourcesUnavailable_throwsError() = testScope.runTest { // When both sources are unavailable networkDataSource.tasks = null localDataSource.tasks = null // The repository throws an error - taskRepository.getTasks() + taskRepository.getAll() } @Test - fun getTasks_refreshesLocalDataSource() = testScope.runTest { + fun whenGettingAllTasks_refreshesLocalDataSource() = testScope.runTest { // Forcing an update will fetch tasks from remote val expectedTasks = networkTasks.toExternal() - val newTasks = taskRepository.getTasks(true) + val newTasks = taskRepository.getAll(true) assertEquals(expectedTasks, newTasks) assertEquals(expectedTasks, localDataSource.tasks?.toExternal()) @@ -177,63 +177,63 @@ class DefaultTaskRepositoryTest { @Test fun completeTask_completesTaskToServiceAPIUpdatesCache() = testScope.runTest { // Save a task - val newTaskId = taskRepository.createTask(newTask.title, newTask.description) + val newTaskId = taskRepository.create(newTask.title, newTask.description) // Make sure it's active - assertThat(taskRepository.getTask(newTaskId)?.isCompleted).isFalse() + assertThat(taskRepository.get(newTaskId)?.isCompleted).isFalse() // Mark is as complete - taskRepository.completeTask(newTaskId) + taskRepository.complete(newTaskId) // Verify it's now completed - assertThat(taskRepository.getTask(newTaskId)?.isCompleted).isTrue() + assertThat(taskRepository.get(newTaskId)?.isCompleted).isTrue() } @Test fun completeTask_activeTaskToServiceAPIUpdatesCache() = testScope.runTest { // Save a task - val newTaskId = taskRepository.createTask(newTask.title, newTask.description) - taskRepository.completeTask(newTaskId) + val newTaskId = taskRepository.create(newTask.title, newTask.description) + taskRepository.complete(newTaskId) // Make sure it's completed - assertThat(taskRepository.getTask(newTaskId)?.isActive).isFalse() + assertThat(taskRepository.get(newTaskId)?.isActive).isFalse() // Mark is as active - taskRepository.activateTask(newTaskId) + taskRepository.activate(newTaskId) // Verify it's now activated - assertThat(taskRepository.getTask(newTaskId)?.isActive).isTrue() + assertThat(taskRepository.get(newTaskId)?.isActive).isTrue() } @Test - fun getTask_repositoryCachesAfterFirstApiCall() = testScope.runTest { + fun whenGettingATask_repositoryCachesAfterFirstApiCall() = testScope.runTest { // Obtain a task from the local data source localDataSource = FakeTaskDao(mutableListOf(task1.toLocal())) - val initial = taskRepository.getTask(task1.id) + val initial = taskRepository.get(task1.id) // Change the tasks on the remote networkDataSource.tasks = newTasks.toNetwork().toMutableList() // Obtain the same task again - val second = taskRepository.getTask(task1.id) + val second = taskRepository.get(task1.id) // Initial and second tasks should match because we didn't force a refresh assertThat(second).isEqualTo(initial) } @Test - fun getTask_forceRefresh() = testScope.runTest { + fun whenGettingATask_aRefreshOccurs() = testScope.runTest { // Trigger the repository to load data, which loads from remote and caches networkDataSource.tasks = mutableListOf(task1.toNetwork()) - val task1FirstTime = taskRepository.getTask(task1.id, forceUpdate = true) + val task1FirstTime = taskRepository.get(task1.id, forceUpdate = true) assertThat(task1FirstTime?.id).isEqualTo(task1.id) // Configure the remote data source to return a different task networkDataSource.tasks = mutableListOf(task2.toNetwork()) // Force refresh - val task1SecondTime = taskRepository.getTask(task1.id, true) - val task2SecondTime = taskRepository.getTask(task2.id, true) + val task1SecondTime = taskRepository.get(task1.id, true) + val task2SecondTime = taskRepository.get(task2.id, true) // Only task2 works because task1 does not exist on the remote assertThat(task1SecondTime).isNull() @@ -244,9 +244,9 @@ class DefaultTaskRepositoryTest { fun clearCompletedTasks() = testScope.runTest { val completedTask = task1.copy(isCompleted = true) localDataSource.tasks = listOf(completedTask.toLocal(), task2.toLocal()) - taskRepository.clearCompletedTasks() + taskRepository.clearAllCompleted() - val tasks = taskRepository.getTasks(true) + val tasks = taskRepository.getAll(true) assertThat(tasks).hasSize(1) assertThat(tasks).contains(task2) @@ -255,28 +255,28 @@ class DefaultTaskRepositoryTest { @Test fun deleteAllTasks() = testScope.runTest { - val initialTasks = taskRepository.getTasks() + val initialTasks = taskRepository.getAll() // Verify tasks are returned assertThat(initialTasks.size).isEqualTo(1) // Delete all tasks - taskRepository.deleteAllTasks() + taskRepository.deleteAll() // Verify tasks are empty now - val afterDeleteTasks = taskRepository.getTasks() + val afterDeleteTasks = taskRepository.getAll() assertThat(afterDeleteTasks).isEmpty() } @Test fun deleteSingleTask() = testScope.runTest { - val initialTasksSize = taskRepository.getTasks(true).size + val initialTasksSize = taskRepository.getAll(true).size // Delete first task - taskRepository.deleteTask(task1.id) + taskRepository.delete(task1.id) // Fetch data again - val afterDeleteTasks = taskRepository.getTasks(true) + val afterDeleteTasks = taskRepository.getAll(true) // Verify only one task was deleted assertThat(afterDeleteTasks.size).isEqualTo(initialTasksSize - 1) diff --git a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModelTest.kt b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModelTest.kt index c9a2c7618..6c3a64914 100644 --- a/app/src/test/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModelTest.kt +++ b/app/src/test/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailViewModelTest.kt @@ -90,7 +90,7 @@ class TaskDetailViewModelTest { @Test fun activateTask() = runTest { - tasksRepository.deleteAllTasks() + tasksRepository.deleteAll() tasksRepository.addTasks(task.copy(isCompleted = true)) // Verify that the task was completed initially @@ -101,7 +101,7 @@ class TaskDetailViewModelTest { taskDetailViewModel.setCompleted(false) // Then the task is not completed and the snackbar shows the correct message - val newTask = tasksRepository.getTask(task.id) + val newTask = tasksRepository.get(task.id) assertTrue((newTask?.isActive) ?: false) assertThat(taskDetailViewModel.uiState.first().userMessage) .isEqualTo(R.string.task_marked_active) diff --git a/shared-test/src/main/java/com/example/android/architecture/blueprints/todoapp/data/FakeTaskRepository.kt b/shared-test/src/main/java/com/example/android/architecture/blueprints/todoapp/data/FakeTaskRepository.kt index 20056ff7d..c071b0bd2 100644 --- a/shared-test/src/main/java/com/example/android/architecture/blueprints/todoapp/data/FakeTaskRepository.kt +++ b/shared-test/src/main/java/com/example/android/architecture/blueprints/todoapp/data/FakeTaskRepository.kt @@ -52,11 +52,11 @@ class FakeTaskRepository : TaskRepository { // Tasks already refreshed } - override suspend fun refreshTask(taskId: String) { + override suspend fun refresh(taskId: String) { refresh() } - override suspend fun createTask(title: String, description: String): String { + override suspend fun create(title: String, description: String): String { val taskId = generateTaskId() Task(title = title, description = description, id = taskId).also { saveTask(it) @@ -64,29 +64,29 @@ class FakeTaskRepository : TaskRepository { return taskId } - override fun getTasksStream(): Flow> = observableTasks + override fun observeAll(): Flow> = observableTasks - override fun getTaskStream(taskId: String): Flow { + override fun observe(taskId: String): Flow { return observableTasks.map { tasks -> return@map tasks.firstOrNull { it.id == taskId } } } - override suspend fun getTask(taskId: String, forceUpdate: Boolean): Task? { + override suspend fun get(taskId: String, forceUpdate: Boolean): Task? { if (shouldThrowError) { throw Exception("Test exception") } return savedTasks.value[taskId] } - override suspend fun getTasks(forceUpdate: Boolean): List { + override suspend fun getAll(forceUpdate: Boolean): List { if (shouldThrowError) { throw Exception("Test exception") } return observableTasks.first() } - override suspend fun updateTask(taskId: String, title: String, description: String) { + override suspend fun update(taskId: String, title: String, description: String) { val updatedTask = _savedTasks.value[taskId]?.copy( title = title, description = description @@ -103,19 +103,19 @@ class FakeTaskRepository : TaskRepository { } } - override suspend fun completeTask(taskId: String) { + override suspend fun complete(taskId: String) { _savedTasks.value[taskId]?.let { saveTask(it.copy(isCompleted = true)) } } - override suspend fun activateTask(taskId: String) { + override suspend fun activate(taskId: String) { _savedTasks.value[taskId]?.let { saveTask(it.copy(isCompleted = false)) } } - override suspend fun clearCompletedTasks() { + override suspend fun clearAllCompleted() { _savedTasks.update { tasks -> tasks.filterValues { !it.isCompleted @@ -123,7 +123,7 @@ class FakeTaskRepository : TaskRepository { } } - override suspend fun deleteTask(taskId: String) { + override suspend fun delete(taskId: String) { _savedTasks.update { tasks -> val newTasks = LinkedHashMap(tasks) newTasks.remove(taskId) @@ -131,7 +131,7 @@ class FakeTaskRepository : TaskRepository { } } - override suspend fun deleteAllTasks() { + override suspend fun deleteAll() { _savedTasks.update { LinkedHashMap() }