Implementing the Paging library in your app should be paired with a robust
testing strategy. You should test data loading components such as
PagingSource
and
RemoteMediator
to ensure that they work as expected. You should also write end-to-end tests to
verify that all of the components in your Paging implementation work correctly
together without unexpected side effects.
This guide explains how to test the Paging library in the different architecture layers of your app as well as how to write end-to-end tests for your entire Paging implementation.
UI layer tests
Data fetched with the Paging library is consumed in the UI as
a Flow<PagingData<Value>>
.
To write tests to verify the data in the UI is as you expect, include the
paging-testing
dependency.
It contains the asSnapshot()
extension on a Flow<PagingData<Value>>
. It
offers APIs in its lambda receiver that allow for mocking scrolling
interactions. It returns a standard List<Value>
produced by the scrolling
interactions mocked, which allows you assert the data
paged through contains the expected elements generated by those interactions.
This is illustrated in the following snippet:
fun test_items_contain_one_to_ten() = runTest {
// Get the Flow of PagingData from the ViewModel under test
val items: Flow<PagingData<String>> = viewModel.items
val itemsSnapshot: List<String> = items.asSnapshot {
// Scroll to the 50th item in the list. This will also suspend till
// the prefetch requirement is met if there's one.
// It also suspends until all loading is complete.
scrollTo(index = 50)
}
// With the asSnapshot complete, you can now verify that the snapshot
// has the expected values
assertEquals(
expected = (0..50).map(Int::toString),
actual = itemsSnapshot
)
}
Alternatively, you can scroll until a given predicate is met as seen in the snippet below:
fun test_footer_is_visible() = runTest {
// Get the Flow of PagingData from the ViewModel under test
val items: Flow<PagingData<String>> = viewModel.items
val itemsSnapshot: List<String> = items.asSnapshot {
// Scroll till the footer is visible
appendScrollWhile { item: String -> item != "Footer" }
}
Testing transformations
You should also write unit tests that cover any transformations you apply to
the PagingData
stream. Use the asPagingSourceFactory
extension. This extension is available on the following data types:
List<Value>
.Flow<List<Value>>
.
The choice of which extension to use depends on what you're trying to test. Use:
List<Value>.asPagingSourceFactory()
: If you want to test static transformations likemap()
andinsertSeparators()
on data.Flow<List<Value>>.asPagingSourceFactory()
: If you want to test how updates to your data, like writing to the backing data source, affects your paging pipeline.
To use either of these extensions follow the following pattern:
- Create the
PagingSourceFactory
using the appropriate extension for your needs. - Use the returned
PagingSourceFactory
in a fake for yourRepository
. - Pass that
Repository
to yourViewModel
.
The ViewModel
can then be tested as in covered in the previous section.
Consider the following ViewModel
:
class MyViewModel(
myRepository: myRepository
) {
val items = Pager(
config: PagingConfig,
initialKey = null,
pagingSourceFactory = { myRepository.pagingSource() }
)
.flow
.map { pagingData ->
pagingData.insertSeparators<String, String> { before, _ ->
when {
// Add a dashed String separator if the prior item is a multiple of 10
before.last() == '0' -> "---------"
// Return null to avoid adding a separator between two items.
else -> null
}
}
}
To test the transformation in MyViewModel
, supply a fake instance of
MyRepository
that delegates to a static List
representing the data to be
transformed as shown in the following snippet:
class FakeMyRepository(): MyRepository {
private val items = (0..100).map(Any::toString)
private val pagingSourceFactory = items.asPagingSourceFactory()
val pagingSource = pagingSourceFactory()
}
You can then write a test for the separator logic as in the following snippet:
fun test_separators_are_added_every_10_items() = runTest {
// Create your ViewModel
val viewModel = MyViewModel(
myRepository = FakeMyRepository()
)
// Get the Flow of PagingData from the ViewModel with the separator transformations applied
val items: Flow<PagingData<String>> = viewModel.items
val snapshot: List<String> = items.asSnapshot()
// With the asSnapshot complete, you can now verify that the snapshot
// has the expected separators.
}
Data layer tests
Write unit tests for the components in your data layer to ensure that they
load the data from your data sources appropriately. Provide
fake versions of
dependencies to verify that the components under test function correctly in
isolation. The main components that you need to test in the repository layer are
the PagingSource
and the RemoteMediator
. The examples in the sections to follow are based on the
Paging with Network
Sample.
PagingSource tests
Unit tests for your PagingSource
implementation involve setting up the
PagingSource
instance and loading data from it with a TestPager
.
To set up the PagingSource
instance for testing, provide fake data to the
constructor. This gives you control over the data in your tests.
In following example, the RedditApi
parameter is a Retrofit
interface that defines the server requests and the response classes.
A fake version can implement the interface, override any required functions,
and provide convenience methods to configure how the fake object should react
in tests.
After the fakes are in place, set up the dependencies and initialize the
PagingSource
object in the test. The following example demonstrates
initializing the FakeRedditApi
object with a list of test posts, and testing
the RedditPagingSource
instance:
class SubredditPagingSourceTest {
private val mockPosts = listOf(
postFactory.createRedditPost(DEFAULT_SUBREDDIT),
postFactory.createRedditPost(DEFAULT_SUBREDDIT),
postFactory.createRedditPost(DEFAULT_SUBREDDIT)
)
private val fakeApi = FakeRedditApi().apply {
mockPosts.forEach { post -> addPost(post) }
}
@Test
fun loadReturnsPageWhenOnSuccessfulLoadOfItemKeyedData() = runTest {
val pagingSource = RedditPagingSource(
fakeApi,
DEFAULT_SUBREDDIT
)
val pager = TestPager(CONFIG, pagingSource)
val result = pager.refresh() as LoadResult.Page
// Write assertions against the loaded data
assertThat(result.data)
.containsExactlyElementsIn(mockPosts)
.inOrder()
}
}
The TestPager
also lets you do the following:
- Test consecutive loads from your
PagingSource
:
@Test
fun test_consecutive_loads() = runTest {
val page = with(pager) {
refresh()
append()
append()
} as LoadResult.Page
assertThat(page.data)
.containsExactlyElementsIn(testPosts)
.inOrder()
}
- Test error scenarios in your
PagingSource
:
@Test
fun refresh_returnError() {
val pagingSource = RedditPagingSource(
fakeApi,
DEFAULT_SUBREDDIT
)
// Configure your fake to return errors
fakeApi.setReturnsError()
val pager = TestPager(CONFIG, source)
runTest {
source.errorNextLoad = true
val result = pager.refresh()
assertTrue(result is LoadResult.Error)
val page = pager.getLastLoadedPage()
assertThat(page).isNull()
}
}
RemoteMediator tests
The goal of the RemoteMediator
unit tests is to verify that the load()
function returns the correct
MediatorResult
.
Tests for side effects, such as data being inserted into the database, are
better suited for integration tests.
The first step is to determine what dependencies your RemoteMediator
implementation needs. The following example demonstrates a RemoteMediator
implementation that requires a Room database, a Retrofit interface, and a search
string:
Kotlin
@OptIn(ExperimentalPagingApi::class) class PageKeyedRemoteMediator( private val db: RedditDb, private val redditApi: RedditApi, private val subredditName: String ) : RemoteMediator<Int, RedditPost>() { ... }
Java
public class PageKeyedRemoteMediator extends RxRemoteMediator<Integer, RedditPost> { @NonNull private RedditDb db; @NonNull private RedditPostDao postDao; @NonNull private SubredditRemoteKeyDao remoteKeyDao; @NonNull private RedditApi redditApi; @NonNull private String subredditName; public PageKeyedRemoteMediator( @NonNull RedditDb db, @NonNull RedditApi redditApi, @NonNull String subredditName ) { this.db = db; this.postDao = db.posts(); this.remoteKeyDao = db.remoteKeys(); this.redditApi = redditApi; this.subredditName = subredditName; ... } }
Java
public class PageKeyedRemoteMediator extends ListenableFutureRemoteMediator<Integer, RedditPost> { @NonNull private RedditDb db; @NonNull private RedditPostDao postDao; @NonNull private SubredditRemoteKeyDao remoteKeyDao; @NonNull private RedditApi redditApi; @NonNull private String subredditName; @NonNull private Executor bgExecutor; public PageKeyedRemoteMediator( @NonNull RedditDb db, @NonNull RedditApi redditApi, @NonNull String subredditName, @NonNull Executor bgExecutor ) { this.db = db; this.postDao = db.posts(); this.remoteKeyDao = db.remoteKeys(); this.redditApi = redditApi; this.subredditName = subredditName; this.bgExecutor = bgExecutor; ... } }
You can provide the Retrofit interface and the search string as demonstrated in
the PagingSource tests section. Providing a mock version
of the Room database is very involved, so it can be easier to provide an
in-memory implementation of
the database instead of a full mock version. Because creating a Room database
requires a Context
object, you must
place this RemoteMediator
test in the androidTest
directory and execute it
with the AndroidJUnit4 test runner so that it has access to a test application
context. For more information about instrumented tests, see Build instrumented
unit tests.
Define tear-down functions to ensure that state does not leak between test functions. This ensures consistent results between test runs.
Kotlin
@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 = mockRedditApi() 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() } }
Java
@RunWith(AndroidJUnit4.class) public class PageKeyedRemoteMediatorTest { static PostFactory postFactory = new PostFactory(); static List<RedditPost> mockPosts = new ArrayList<>(); static MockRedditApi mockApi = new MockRedditApi(); private RedditDb mockDb = RedditDb.Companion.create( ApplicationProvider.getApplicationContext(), true ); static { for (int i=0; i<3; i++) { RedditPost post = postFactory.createRedditPost(DEFAULT_SUBREDDIT); mockPosts.add(post); } } @After public void tearDown() { mockDb.clearAllTables(); // Clear the failure message after each test run. mockApi.setFailureMsg(null); // Clear out posts after each test run. mockApi.clearPosts(); } }
Java
@RunWith(AndroidJUnit4.class) public class PageKeyedRemoteMediatorTest { static PostFactory postFactory = new PostFactory(); static List<RedditPost> mockPosts = new ArrayList<>(); static MockRedditApi mockApi = new MockRedditApi(); private RedditDb mockDb = RedditDb.Companion.create( ApplicationProvider.getApplicationContext(), true ); static { for (int i=0; i<3; i++) { RedditPost post = postFactory.createRedditPost(DEFAULT_SUBREDDIT); mockPosts.add(post); } } @After public void tearDown() { mockDb.clearAllTables(); // Clear the failure message after each test run. mockApi.setFailureMsg(null); // Clear out posts after each test run. mockApi.clearPosts(); } }
The next step is to test the load()
function. In this example, there are three
cases to test:
- The first case is when
mockApi
returns valid data. Theload()
function should returnMediatorResult.Success
, and theendOfPaginationReached
property should befalse
. - The second case is when
mockApi
returns a successful response, but the returned data is empty. Theload()
function should returnMediatorResult.Success
, and theendOfPaginationReached
property should betrue
. - The third case is when
mockApi
throws an exception when fetching the data. Theload()
function should returnMediatorResult.Error
.
Follow these steps to test the first case:
- Set up the
mockApi
with the post data to return. - Initialize the
RemoteMediator
object. - Test the
load()
function.
Kotlin
@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<Int, RedditPost>( listOf(), null, PagingConfig(10), 10 ) val result = remoteMediator.load(LoadType.REFRESH, pagingState) assertTrue { result is MediatorResult.Success } assertFalse { (result as MediatorResult.Success).endOfPaginationReached } }
Java
@Test public void refreshLoadReturnsSuccessResultWhenMoreDataIsPresent() throws InterruptedException { // Add mock results for the API to return. for (RedditPost post: mockPosts) { mockApi.addPost(post); } PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); remoteMediator.loadSingle(LoadType.REFRESH, pagingState) .test() .await() .assertValueCount(1) .assertValue(value -> value instanceof RemoteMediator.MediatorResult.Success && ((RemoteMediator.MediatorResult.Success) value).endOfPaginationReached() == false); }
Java
@Test public void refreshLoadReturnsSuccessResultWhenMoreDataIsPresent() throws InterruptedException, ExecutionException { // Add mock results for the API to return. for (RedditPost post: mockPosts) { mockApi.addPost(post); } PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT, new CurrentThreadExecutor() ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); RemoteMediator.MediatorResult result = remoteMediator.loadFuture(LoadType.REFRESH, pagingState).get(); assertThat(result, instanceOf(RemoteMediator.MediatorResult.Success.class)); assertFalse(((RemoteMediator.MediatorResult.Success) result).endOfPaginationReached()); }
The second test requires the mockApi
to return an empty result. Because you
clear the data from the mockApi
after each test run, it will return an empty
result by default.
Kotlin
@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<Int, RedditPost>( listOf(), null, PagingConfig(10), 10 ) val result = remoteMediator.load(LoadType.REFRESH, pagingState) assertTrue { result is MediatorResult.Success } assertTrue { (result as MediatorResult.Success).endOfPaginationReached } }
Java
@Test public void refreshLoadSuccessAndEndOfPaginationWhenNoMoreData() throws InterruptedException() { // To test endOfPaginationReached, don't set up the mockApi to return post // data here. PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); remoteMediator.loadSingle(LoadType.REFRESH, pagingState) .test() .await() .assertValueCount(1) .assertValue(value -> value instanceof RemoteMediator.MediatorResult.Success && ((RemoteMediator.MediatorResult.Success) value).endOfPaginationReached() == true); }
Java
@Test public void refreshLoadSuccessAndEndOfPaginationWhenNoMoreData() throws InterruptedException, ExecutionException { // To test endOfPaginationReached, don't set up the mockApi to return post // data here. PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT, new CurrentThreadExecutor() ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); RemoteMediator.MediatorResult result = remoteMediator.loadFuture(LoadType.REFRESH, pagingState).get(); assertThat(result, instanceOf(RemoteMediator.MediatorResult.Success.class)); assertTrue(((RemoteMediator.MediatorResult.Success) result).endOfPaginationReached()); }
The final test requires the mockApi
to throw an exception so that the test can
verify that the load()
function correctly returns MediatorResult.Error
.
Kotlin
@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<Int, RedditPost>( listOf(), null, PagingConfig(10), 10 ) val result = remoteMediator.load(LoadType.REFRESH, pagingState) assertTrue {result is MediatorResult.Error } }
Java
@Test public void refreshLoadReturnsErrorResultWhenErrorOccurs() throws InterruptedException { // Set up failure message to throw exception from the mock API. mockApi.setFailureMsg("Throw test failure"); PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); remoteMediator.loadSingle(LoadType.REFRESH, pagingState) .test() .await() .assertValueCount(1) .assertValue(value -> value instanceof RemoteMediator.MediatorResult.Error); }
Java
@Test public void refreshLoadReturnsErrorResultWhenErrorOccurs() throws InterruptedException, ExecutionException { // Set up failure message to throw exception from the mock API. mockApi.setFailureMsg("Throw test failure"); PageKeyedRemoteMediator remoteMediator = new PageKeyedRemoteMediator( mockDb, mockApi, SubRedditViewModel.DEFAULT_SUBREDDIT, new CurrentThreadExecutor() ); PagingState<Integer, RedditPost> pagingState = new PagingState<>( new ArrayList(), null, new PagingConfig(10), 10 ); RemoteMediator.MediatorResult result = remoteMediator.loadFuture(LoadType.REFRESH, pagingState).get(); assertThat(result, instanceOf(RemoteMediator.MediatorResult.Error.class)); }
End-to-end tests
Unit tests provide assurance that individual Paging components work in isolation, but end-to-end tests provide more confidence that the application works as a whole. These tests will still need some mock dependencies, but generally they will cover most of your app code.
The example in this section uses a mock API dependency to avoid using the network in tests. The mock API is configured to return a consistent set of test data, resulting in repeatable tests. Decide which dependencies to swap out for mock implementations based on what each dependency does, how consistent its output is, and how much fidelity you need from your tests.
Write your code in a way that allows you to easily swap in mock versions of your dependencies. The following example uses a basic service locator implementation to provide and change dependencies as needed. In larger apps, using a dependency injection library like Hilt can help manage more-complex dependency graphs.
Kotlin
class RedditActivityTest { companion object { private const val TEST_SUBREDDIT = "test" } private val postFactory = PostFactory() private val mockApi = MockRedditApi().apply { addPost(postFactory.createRedditPost(DEFAULT_SUBREDDIT)) addPost(postFactory.createRedditPost(TEST_SUBREDDIT)) addPost(postFactory.createRedditPost(TEST_SUBREDDIT)) } @Before fun init() { val app = ApplicationProvider.getApplicationContext<Application>() // Use a controlled service locator with a mock API. ServiceLocator.swap( object : DefaultServiceLocator(app = app, useInMemoryDb = true) { override fun getRedditApi(): RedditApi = mockApi } ) } }
Java
public class RedditActivityTest { public static final String TEST_SUBREDDIT = "test"; private static PostFactory postFactory = new PostFactory(); private static MockRedditApi mockApi = new MockRedditApi(); static { mockApi.addPost(postFactory.createRedditPost(DEFAULT_SUBREDDIT)); mockApi.addPost(postFactory.createRedditPost(TEST_SUBREDDIT)); mockApi.addPost(postFactory.createRedditPost(TEST_SUBREDDIT)); } @Before public void setup() { Application app = ApplicationProvider.getApplicationContext(); // Use a controlled service locator with a mock API. ServiceLocator.Companion.swap( new DefaultServiceLocator(app, true) { @NotNull @Override public RedditApi getRedditApi() { return mockApi; } } ); } }
Java
public class RedditActivityTest { public static final String TEST_SUBREDDIT = "test"; private static PostFactory postFactory = new PostFactory(); private static MockRedditApi mockApi = new MockRedditApi(); static { mockApi.addPost(postFactory.createRedditPost(DEFAULT_SUBREDDIT)); mockApi.addPost(postFactory.createRedditPost(TEST_SUBREDDIT)); mockApi.addPost(postFactory.createRedditPost(TEST_SUBREDDIT)); } @Before public void setup() { Application app = ApplicationProvider.getApplicationContext(); // Use a controlled service locator with a mock API. ServiceLocator.Companion.swap( new DefaultServiceLocator(app, true) { @NotNull @Override public RedditApi getRedditApi() { return mockApi; } } ); } }
After you set up the test structure, the next step is to verify that the data
returned by the Pager
implementation is correct. One test should ensure that
the Pager
object loads the default data when the page first loads, and another
test should verify that the Pager
object correctly loads additional data based
on user input. In the following example, the test verifies that the Pager
object updates the RecyclerView.Adapter
with the correct number of items
returned from the API when the user enters a different subreddit to search.
Kotlin
@Test fun loadsTheDefaultResults() { ActivityScenario.launch(RedditActivity::class.java) onView(withId(R.id.list)).check { view, noViewFoundException -> if (noViewFoundException != null) { throw noViewFoundException } val recyclerView = view as RecyclerView assertEquals(1, recyclerView.adapter?.itemCount) } } @Test // Verify that the default data is swapped out when the user searches for a // different subreddit. fun loadsTheTestResultsWhenSearchingForSubreddit() { ActivityScenario.launch(RedditActivity::class.java ) onView(withId(R.id.list)).check { view, noViewFoundException -> if (noViewFoundException != null) { throw noViewFoundException } val recyclerView = view as RecyclerView // Verify that it loads the default data first. assertEquals(1, recyclerView.adapter?.itemCount) } // Search for test subreddit instead of default to trigger new data load. onView(withId(R.id.input)).perform( replaceText(TEST_SUBREDDIT), pressKey(KeyEvent.KEYCODE_ENTER) ) onView(withId(R.id.list)).check { view, noViewFoundException -> if (noViewFoundException != null) { throw noViewFoundException } val recyclerView = view as RecyclerView assertEquals(2, recyclerView.adapter?.itemCount) } }
Java
@Test public void loadsTheDefaultResults() { ActivityScenario.launch(RedditActivity.class); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; assertEquals(1, recyclerView.getAdapter().getItemCount()); }); } @Test // Verify that the default data is swapped out when the user searches for a // different subreddit. public void loadsTheTestResultsWhenSearchingForSubreddit() { ActivityScenario.launch(RedditActivity.class); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; // Verify that it loads the default data first. assertEquals(1, recyclerView.getAdapter().getItemCount()); }); // Search for test subreddit instead of default to trigger new data load. onView(withId(R.id.input)).perform( replaceText(TEST_SUBREDDIT), pressKey(KeyEvent.KEYCODE_ENTER) ); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; assertEquals(2, recyclerView.getAdapter().getItemCount()); }); }
Java
@Test public void loadsTheDefaultResults() { ActivityScenario.launch(RedditActivity.class); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; assertEquals(1, recyclerView.getAdapter().getItemCount()); }); } @Test // Verify that the default data is swapped out when the user searches for a // different subreddit. public void loadsTheTestResultsWhenSearchingForSubreddit() { ActivityScenario.launch(RedditActivity.class); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; // Verify that it loads the default data first. assertEquals(1, recyclerView.getAdapter().getItemCount()); }); // Search for test subreddit instead of default to trigger new data load. onView(withId(R.id.input)).perform( replaceText(TEST_SUBREDDIT), pressKey(KeyEvent.KEYCODE_ENTER) ); onView(withId(R.id.list)).check((view, noViewFoundException) -> { if (noViewFoundException != null) { throw noViewFoundException; } RecyclerView recyclerView = (RecyclerView) view; assertEquals(2, recyclerView.getAdapter().getItemCount()); }); }
Instrumented tests should verify that the data displays correctly in the UI. Do
this either by verifying that the correct number of items exists in the
RecyclerView.Adapter
, or by iterating through the individual row views and
verifying that the data is formatted correctly.
Recommended for you
- Note: link text is displayed when JavaScript is off
- Page from network and database
- Migrate to Paging 3
- Load and display paged data