अपने ऐप्लिकेशन में पेजिंग लाइब्रेरी को लागू करने से जुड़े
टेस्टिंग के लिए रणनीति बनाई गई. आपको डेटा लोड करने वाले कॉम्पोनेंट, जैसे कि
PagingSource
और
RemoteMediator
ताकि यह पक्का किया जा सके कि वे उम्मीद के मुताबिक काम करें. आपको एंड-टू-एंड टेस्ट भी लिखना चाहिए, ताकि
पुष्टि करें कि पेजिंग लागू करने के सभी कॉम्पोनेंट सही तरीके से काम कर रहे हैं
बिना किसी संभावित दुष्प्रभाव के एक साथ कर सकते हैं.
यह गाइड अलग-अलग वर्शन में पेजिंग लाइब्रेरी को टेस्ट करने का तरीका बताती है आपके ऐप्लिकेशन की आर्किटेक्चर लेयर और लिखने का तरीका पेजिंग को लागू करने के आपके सभी तरीकों की एंड-टू-एंड जांच.
यूज़र इंटरफ़ेस (यूआई) लेयर की जांच
पेजिंग लाइब्रेरी से फ़ेच किया गया डेटा यूज़र इंटरफ़ेस (यूआई) में इस तरह इस्तेमाल होता है
Flow<PagingData<Value>>
.
टेस्ट लिखने के लिए, यूज़र इंटरफ़ेस (यूआई) में डेटा आपकी उम्मीद के मुताबिक है, इसकी पुष्टि करने के लिए,
paging-testing
डिपेंडेंसी.
इसमें Flow<PagingData<Value>>
पर मौजूद asSnapshot()
एक्सटेंशन है. यह
यह अपने Lambda रिसीवर में ऐसे एपीआई ऑफ़र करता है जो स्क्रोलिंग को मॉक करने की सुविधा देते हैं
इंटरैक्शन. यह स्क्रोलिंग से बना स्टैंडर्ड 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
बनाएं ज़रूरतें पूरी करता है. - लौटाए गए
PagingSourceFactory
का इस्तेमाल इसमें करें एक नकली आपकेRepository
के लिए. - उस
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
इंस्टेंस और TestPager
के साथ इससे डेटा लोड कर रहा है.
PagingSource
इंस्टेंस को जांच के लिए सेट अप करने के लिए,
कंस्ट्रक्टर है. इससे आपको जांच में इस्तेमाल होने वाले डेटा पर कंट्रोल मिलता है.
यहां दिए गए उदाहरण में, RedditApi
पैरामीटर एक Retrofit है
इंटरफ़ेस, जो सर्वर के अनुरोधों और रिस्पॉन्स क्लास के बारे में बताता है.
नकली वर्शन, इंटरफ़ेस को लागू कर सकता है और किसी भी ज़रूरी फ़ंक्शन को ओवरराइड कर सकता है,
साथ ही, यह कॉन्फ़िगर करने के आसान तरीके उपलब्ध कराता है कि नकली ऑब्जेक्ट को कैसे प्रतिक्रिया देनी चाहिए
का इस्तेमाल किया है.
नकली वैल्यू लागू होने के बाद, डिपेंडेंसी सेट अप करें और
जांच में PagingSource
ऑब्जेक्ट मौजूद है. नीचे दिए गए उदाहरण से पता चलता है कि
टेस्ट पोस्ट की सूची के साथ, 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
किन डिपेंडेंसी
लागू करने की ज़रूरतों को पूरा करता है. नीचे दिए गए उदाहरण में, RemoteMediator
के बारे में बताया गया है
इसे लागू करने के लिए रूम डेटाबेस, Retrofit इंटरफ़ेस, और खोज की ज़रूरत होती है
स्ट्रिंग:
@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 टेस्ट सेक्शन में. मॉक वर्शन उपलब्ध कराया जा रहा है
का डेटाबेस काफ़ी अहम है, इसलिए आपके डेटा की मदद से,
इन-मेमोरी लागू करना
के बजाय डेटाबेस का इस्तेमाल करें. क्योंकि रूम का डेटाबेस बनाया जा रहा है
Context
ऑब्जेक्ट की ज़रूरत होती है, तो आपके लिए यह ज़रूरी है
इस RemoteMediator
टेस्ट को androidTest
डायरेक्ट्री में रखें और इसे एक्ज़ीक्यूट करें
को अपडेट करने के लिए, ताकि उसके पास टेस्ट ऐप्लिकेशन का ऐक्सेस हो
संदर्भ. इंस्ट्रुमेंटेड टेस्ट के बारे में ज़्यादा जानने के लिए, इंस्ट्रुमेंटेड बनाएं
यूनिट टेस्ट का इस्तेमाल करें.
टियर-डाउन फ़ंक्शन को परिभाषित करें, ताकि यह पक्का किया जा सके कि टेस्ट के बीच में कोई भी स्टेट लीक न हो फ़ंक्शन. इससे यह पक्का होता है कि जांच के बीच के नतीजे एक जैसे होंगे.
@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
रिटर्न करना चाहिए.
पहले केस की जांच करने के लिए, यह तरीका अपनाएं:
- पाने के लिए, पोस्ट के डेटा के साथ
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));
}
शुरू से अंत तक के टेस्ट
यूनिट टेस्ट से यह पक्का होता है कि अलग-अलग पेजिंग कॉम्पोनेंट सही तरीके से काम करते हैं आइसोलेशन के साथ-साथ शुरू से लेकर आखिर तक जांच करने से लोग इस पर ज़्यादा भरोसा करते हैं कि ऐप्लिकेशन काम करती है. इन टेस्ट के लिए अब भी कुछ मॉक डिपेंडेंसी की ज़रूरत होगी, लेकिन आम तौर पर, वे आपके ऐप्लिकेशन कोड के ज़्यादातर हिस्से को कवर कर लेते हैं.
इस सेक्शन में दिए गए उदाहरण में, नकली एपीआई डिपेंडेंसी का इस्तेमाल किया गया है, ताकि नेटवर्क की जांच की जा रही है. मॉक एपीआई को इस तरह से कॉन्फ़िगर किया गया है कि वह टेस्ट का एक जैसा सेट दिखा सके जिससे अलग-अलग तरह के टेस्ट का डेटा मिलता है. यह तय करना कि किन डिपेंडेंसी का इस्तेमाल करना है हर डिपेंडेंसी के आधार पर मॉक इंप्लीमेंटेशन: से पता चलता है कि आपको अपने टेस्ट से कितनी फ़िडेलिटी की ज़रूरत है.
अपने कोड को इस तरह लिखें कि आप अपने कोड के मॉक वर्शन की अदला-बदली कर सकें निर्भरता. नीचे दिए गए उदाहरण में बुनियादी सर्विस लोकेटर का इस्तेमाल किया गया है लागू करने के लिए ज़रूरत के हिसाब से डिपेंडेंसी बदलें. बड़े ऐप्लिकेशन में, डिपेंडेंसी इंजेक्शन का इस्तेमाल करना 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
ऑब्जेक्ट,
उपयोगकर्ता के इनपुट के आधार पर. यहां दिए गए उदाहरण में, टेस्ट से इस बात की पुष्टि होती है कि Pager
ऑब्जेक्ट, 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 खाते में साइन इन करने की कोशिश करें.