在应用中实现 Paging 库需要辅以稳健的测试策略。您应测试 PagingSource
和 RemoteMediator
等数据加载组件,以确保这些组件按预期运行。您还应编写端到端测试,以验证 Paging 实现中的所有组件能否正确协作,而不产生意外的副效应。
本指南将介绍如何在应用的不同架构层中测试 Paging 库,以及如何为整个 Paging 实现编写端到端测试。
界面层测试
使用 Paging 库提取的数据会作为 Flow<PagingData<Value>>
在界面中使用。若要编写测试以验证界面中的数据是否符合您的预期,请添加 paging-testing
依赖项。它包含对 Flow<PagingData<Value>>
的 asSnapshot()
扩展,并且其 lambda 接收器中提供了一些 API,这些 API 用于模拟滚动互动。它会返回一个由模拟的滚动互动生成的标准 List<Value>
,让您可以断言分页数据包含由这些互动生成的预期元素。如以下代码段所示:
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
)
}
或者,您也可以一直滚动,直至满足给定的谓词,如以下代码段所示:
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" }
}
测试转换
您还应编写单元测试,以涵盖您应用于 PagingData
流的所有转换。使用 asPagingSourceFactory
扩展。此扩展适用于以下数据类型:
List<Value>
。Flow<List<Value>>
。
具体选择使用哪一个扩展取决于您要测试的内容。 使用:
List<Value>.asPagingSourceFactory()
:如果要测试数据的静态转换(如map()
和insertSeparators()
)。Flow<List<Value>>.asPagingSourceFactory()
:如果要测试数据更新(例如写入后备数据源)对分页流水线有何影响。
如需使用上述任何一种扩展,请遵循以下模式:
- 根据您的需求使用适当的扩展来创建
PagingSourceFactory
。 - 在
Repository
的虚构实例中使用返回的PagingSourceFactory
。 - 将该
Repository
传递给ViewModel
。
然后,您可按照上一部分中介绍的方式测试 ViewModel
。请考虑以下 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
}
}
}
若要在 MyViewModel
中测试转换,请提供 MyRepository
的虚构实例以委托给代表要转换的数据的 List
,如以下代码段所示:
class FakeMyRepository(): MyRepository {
private val items = (0..100).map(Any::toString)
private val pagingSourceFactory = items.asPagingSourceFactory()
val pagingSource = pagingSourceFactory()
}
然后,您可编写针对分隔符逻辑的测试,如以下代码段所示:
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.
}
数据层测试
为数据层中的组件编写单元测试,以确保这些组件能从数据源正确加载数据。提供依赖项的虚构版本,以验证被测组件在单独运行时是否正常。您需要在代码库层中测试的主要组件是 PagingSource
和 RemoteMediator
。后续部分中的示例基于使用网络进行分页的示例。
PagingSource 测试
PagingSource
实现的单元测试涉及设置 PagingSource
实例,以及使用 TestPager
从该实例加载数据。
如需设置 PagingSource
实例以进行测试,请向构造函数提供虚构数据。这样,您就可以控制测试中的数据。在以下示例中,RedditApi
参数是一个 Retrofit
该接口定义了服务器请求和响应类。
虚构版本可以实现该接口、替换任何必需函数,并提供便捷方法来配置虚构对象在测试中的反应方式。
创建好虚构对象后,应在测试中设置依赖项并初始化 PagingSource
对象。以下示例演示了如何使用一系列测试 post 来初始化 FakeRedditApi
对象,以及如何测试 RedditPagingSource
实例:
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()
}
}
借助 TestPager
,您还可以执行以下操作:
- 测试从
PagingSource
的连续加载情况:
@Test
fun test_consecutive_loads() = runTest {
val page = with(pager) {
refresh()
append()
append()
} as LoadResult.Page
assertThat(page.data)
.containsExactlyElementsIn(testPosts)
.inOrder()
}
- 测试
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 测试
RemoteMediator
单元测试的目的是验证 load()
函数是否会返回正确的 MediatorResult
。副效应测试(例如将数据插入数据库)更适合集成测试。
第一步是确定 RemoteMediator
实现需要的依赖项。以下示例演示了需要 Room 数据库、Retrofit 接口和搜索字符串的 RemoteMediator
实现:
@OptIn(ExperimentalPagingApi::class)
class PageKeyedRemoteMediator(
private val db: RedditDb,
private val redditApi: RedditApi,
private val subredditName: String
) : RemoteMediator<Int, RedditPost>() {
...
}
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;
...
}
}
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;
...
}
}
您可以提供 Retrofit 接口和搜索字符串,如 PagingSource 测试部分所示。提供 Room 数据库的模拟版本非常复杂,而更简单的做法是提供数据库的内存中实现,而不是完整的模拟版本。由于创建 Room 数据库需要 Context
对象,您必须将此 RemoteMediator
测试放在 androidTest
目录中,并使用 AndroidJUnit4 测试运行程序来执行此测试,使其能够访问测试应用上下文。如需详细了解插桩测试,请参阅构建插桩单元测试。
定义拆解函数,以确保状态不会在测试函数之间泄漏。这可确保测试运行之间结果的一致。
@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()
}
}
@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();
}
}
@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();
}
}
下一步是测试 load()
函数。在此示例中,要测试三种情况:
- 第一种情况是
mockApi
返回有效数据。load()
函数应返回MediatorResult.Success
,endOfPaginationReached
属性应为false
。 - 第二种情况是
mockApi
返回成功响应,但返回的数据为空。load()
函数应返回MediatorResult.Success
,endOfPaginationReached
属性应为true
。 - 第三种情况是
mockApi
在获取数据时抛出异常。load()
函数应返回MediatorResult.Error
。
请按以下步骤测试第一种情况:
- 使用要返回的 post 数据设置
mockApi
。 - 初始化
RemoteMediator
对象: - 测试
load()
函数。
@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 }
}
@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);
}
@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());
}
第二个测试要求 mockApi
返回一个空结果。由于您在每次运行测试后都清除 mockApi
中的数据,因此默认情况下它会返回一个空结果。
@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 }
}
@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);
}
@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());
}
最后一项测试要求 mockApi
抛出异常,以便测试可以验证 load()
函数是否会正确返回 MediatorResult.Error
。
@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 }
}
@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);
}
@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));
}
端到端测试
单元测试可确保各个 Paging 组件在单独运行时可以正常工作,而端到端测试则可确保应用作为一个整体运行正常。这些测试仍需要一些模拟依赖项,但通常会涵盖您的大部分应用代码。
本部分中的示例使用模拟 API 依赖项,以避免在测试中使用网络。模拟 API 配置为返回一组一致的测试数据,从而使测试具有可重复性。根据每个依赖项的功能、输出的一致性以及测试所需的准确性,确定要为模拟实现换出哪些依赖项。
以方便换入依赖项模拟版本的方式编写代码。以下示例使用基本的服务定位器实现来按需提供和更改依赖项。在较大的应用中,使用 Hilt 等依赖项注入库,有助于管理更复杂的依赖项关系图。
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
}
)
}
}
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;
}
}
);
}
}
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;
}
}
);
}
}
设置测试结构后,下一步是验证 Pager
实现返回的数据是否正确。应有一个测试来确保在页面首次加载时 Pager
对象会加载默认数据,还应有一个测试来验证 Pager
对象是否会根据用户输入正确加载其他数据。在以下示例中,测试会验证:当用户输入不同的 subreddit 进行搜索时,Pager
对象是否会使用从 API 返回的正确数量的项来更新 RecyclerView.Adapter
。
@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)
}
}
@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());
});
}
@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());
});
}
插桩测试应验证数据是否在界面中正确显示。您可以通过以下两种方式之一来实现此目的:验证 RecyclerView.Adapter
中是否存在正确数量的项,或者遍历各个行视图并验证数据格式是否正确。
目前没有任何推荐文档页面。
请尝试登录您的 Google 账号。