diff --git a/PagingWithNetworkSample/app/build.gradle b/PagingWithNetworkSample/app/build.gradle index ca13b5b28..867f2666d 100644 --- a/PagingWithNetworkSample/app/build.gradle +++ b/PagingWithNetworkSample/app/build.gradle @@ -89,6 +89,7 @@ dependencies { androidTestImplementation deps.atsl.rules androidTestImplementation deps.arch_core.testing androidTestImplementation deps.espresso.contrib + androidTestImplementation deps.coroutines.test testImplementation deps.junit testImplementation deps.coroutines.test diff --git a/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PageKeyedRemoteMediatorTest.kt b/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PageKeyedRemoteMediatorTest.kt new file mode 100644 index 000000000..0b0516e7c --- /dev/null +++ b/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PageKeyedRemoteMediatorTest.kt @@ -0,0 +1,108 @@ +package com.android.example.paging.pagingwithnetwork.reddit.ui + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.example.paging.pagingwithnetwork.reddit.db.RedditDb +import com.android.example.paging.pagingwithnetwork.reddit.repository.inDb.PageKeyedRemoteMediator +import com.android.example.paging.pagingwithnetwork.reddit.vo.RedditPost +import com.android.example.paging.pagingwithnetwork.repository.FakeRedditApi +import com.android.example.paging.pagingwithnetwork.repository.PostFactory +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalPagingApi +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class PageKeyedRemoteMediatorTest { + private val postFactory = PostFactory() + private val mockPosts = listOf( + postFactory.createRedditPost(SubRedditViewModel.DEFAULT_SUBREDDIT), + postFactory.createRedditPost(SubRedditViewModel.DEFAULT_SUBREDDIT), + postFactory.createRedditPost(SubRedditViewModel.DEFAULT_SUBREDDIT) + ) + private val mockApi = FakeRedditApi() + + private val mockDb = RedditDb.create( + ApplicationProvider.getApplicationContext(), + useInMemory = true + ) + + @After + fun tearDown() { + mockDb.clearAllTables() + // Clear out failure message to default to the successful response. + mockApi.failureMsg = null + // Clear out posts after each test run. + mockApi.clearPosts() + } + + @Test + fun refreshLoadReturnsSuccessResultWhenMoreDataIsPresent() = runTest { + // Add mock results for the API to return. + mockPosts.forEach { post -> mockApi.addPost(post) } + val remoteMediator = PageKeyedRemoteMediator( + mockDb, + mockApi, + SubRedditViewModel.DEFAULT_SUBREDDIT + ) + val pagingState = PagingState( + listOf(), + null, + PagingConfig(10), + 10 + ) + val result = remoteMediator.load(LoadType.REFRESH, pagingState) + assertTrue(result is RemoteMediator.MediatorResult.Success) + assertFalse((result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @Test + fun refreshLoadSuccessAndEndOfPaginationWhenNoMoreData() = runTest { + // To test endOfPaginationReached, don't set up the mockApi to return post + // data here. + val remoteMediator = PageKeyedRemoteMediator( + mockDb, + mockApi, + SubRedditViewModel.DEFAULT_SUBREDDIT + ) + val pagingState = PagingState( + listOf(), + null, + PagingConfig(10), + 10 + ) + val result = remoteMediator.load(LoadType.REFRESH, pagingState) + assertTrue(result is RemoteMediator.MediatorResult.Success) + assertTrue((result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @Test + fun refreshLoadReturnsErrorResultWhenErrorOccurs() = runTest { + // Set up failure message to throw exception from the mock API. + mockApi.failureMsg = "Throw test failure" + val remoteMediator = PageKeyedRemoteMediator( + mockDb, + mockApi, + SubRedditViewModel.DEFAULT_SUBREDDIT + ) + val pagingState = PagingState( + listOf(), + null, + PagingConfig(10), + 10 + ) + val result = remoteMediator.load(LoadType.REFRESH, pagingState) + assertTrue(result is RemoteMediator.MediatorResult.Error) + } + +} diff --git a/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PagingDataTransformTest.kt b/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PagingDataTransformTest.kt new file mode 100644 index 000000000..28702d764 --- /dev/null +++ b/PagingWithNetworkSample/app/src/androidTest/java/com/android/example/paging/pagingwithnetwork/reddit/ui/PagingDataTransformTest.kt @@ -0,0 +1,79 @@ +package com.android.example.paging.pagingwithnetwork.reddit.ui + +import androidx.paging.AsyncPagingDataDiffer +import androidx.paging.PagingData +import androidx.paging.filter +import androidx.paging.map +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListUpdateCallback +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +@ExperimentalCoroutinesApi +class PagingDataTransformTest { + private val testScope = TestScope() + private val testDispatcher = StandardTestDispatcher(testScope.testScheduler) + + @Before + fun setUp() { + Dispatchers.setMain(testDispatcher) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun differTransformsData() = testScope.runTest { + val data = PagingData.from(listOf(1, 2, 3, 4)).myHelperTransformFunction() + val differ = AsyncPagingDataDiffer( + diffCallback = MyDiffCallback(), + updateCallback = NoopListCallback(), + workerDispatcher = Dispatchers.Main + ) + + // You don't need to use lifecycleScope.launch() if you're using + // PagingData.from() + differ.submitData(data) + + // Wait for transforms and the differ to process all updates. + advanceUntilIdle() + assertEquals(listOf(4, 16), differ.snapshot().items) + } +} + +fun PagingData.myHelperTransformFunction(): PagingData { + return this.map { item -> + item * item + }.filter { item -> + item % 2 == 0 + } +} + +class NoopListCallback : ListUpdateCallback { + override fun onChanged(position: Int, count: Int, payload: Any?) {} + override fun onMoved(fromPosition: Int, toPosition: Int) {} + override fun onInserted(position: Int, count: Int) {} + override fun onRemoved(position: Int, count: Int) {} +} + +class MyDiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean { + return oldItem == newItem + } +} diff --git a/PagingWithNetworkSample/app/src/test-common/java/com/android/example/paging/pagingwithnetwork/repository/FakeRedditApi.kt b/PagingWithNetworkSample/app/src/test-common/java/com/android/example/paging/pagingwithnetwork/repository/FakeRedditApi.kt index 8abb5ada8..980279e31 100644 --- a/PagingWithNetworkSample/app/src/test-common/java/com/android/example/paging/pagingwithnetwork/repository/FakeRedditApi.kt +++ b/PagingWithNetworkSample/app/src/test-common/java/com/android/example/paging/pagingwithnetwork/repository/FakeRedditApi.kt @@ -37,6 +37,10 @@ class FakeRedditApi : RedditApi { subreddit.items.add(post) } + fun clearPosts() { + model.clear() + } + private fun findPosts( subreddit: String, limit: Int,