আপনার অ্যাপে পেজিং লাইব্রেরি প্রয়োগ করা একটি শক্তিশালী পরীক্ষার কৌশলের সাথে যুক্ত হওয়া উচিত। আপনার PagingSource
এবং RemoteMediator
মতো ডেটা লোডিং উপাদানগুলি পরীক্ষা করা উচিত যাতে তারা প্রত্যাশা অনুযায়ী কাজ করে। আপনার পেজিং ইমপ্লিমেন্টেশনের সমস্ত উপাদান অপ্রত্যাশিত পার্শ্বপ্রতিক্রিয়া ছাড়াই একসাথে সঠিকভাবে কাজ করছে কিনা তা যাচাই করার জন্য আপনাকে এন্ড-টু-এন্ড পরীক্ষাও লিখতে হবে।
এই নির্দেশিকাটি ব্যাখ্যা করে যে কীভাবে আপনার অ্যাপের বিভিন্ন আর্কিটেকচার স্তরগুলিতে পেজিং লাইব্রেরি পরীক্ষা করতে হয় সেইসাথে কীভাবে আপনার সম্পূর্ণ পেজিং বাস্তবায়নের জন্য এন্ড-টু-এন্ড পরীক্ষা লিখতে হয়।
UI স্তর পরীক্ষা
পেজিং লাইব্রেরি দিয়ে আনা ডেটা UI এ Flow<PagingData<Value>>
হিসাবে ব্যবহার করা হয়। UI-তে ডেটা যাচাই করার জন্য পরীক্ষা লিখতে আপনার প্রত্যাশা অনুযায়ী, paging-testing
নির্ভরতা অন্তর্ভুক্ত করুন। এটি একটি Flow<PagingData<Value>>
-এ asSnapshot()
এক্সটেনশন ধারণ করে। এটি তার ল্যাম্বডা রিসিভারে 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
নকল ব্যবহার করুন। - আপনার
ViewModel
সেইRepository
পাস করুন।
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
বাস্তবায়নের জন্য ইউনিট পরীক্ষাগুলির মধ্যে একটি TestPager
দিয়ে PagingSource
ইনস্ট্যান্স সেট আপ করা এবং এটি থেকে ডেটা লোড করা জড়িত।
পরীক্ষার জন্য 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; ... } }
আপনি পেজিংসোর্স পরীক্ষা বিভাগে প্রদর্শিত রেট্রোফিট ইন্টারফেস এবং অনুসন্ধান স্ট্রিং প্রদান করতে পারেন। রুম ডাটাবেসের একটি উপহাস সংস্করণ সরবরাহ করা খুব জড়িত, তাই সম্পূর্ণ মক সংস্করণের পরিবর্তে ডাটাবেসের একটি ইন-মেমরি বাস্তবায়ন প্রদান করা সহজ হতে পারে। যেহেতু একটি রুম ডাটাবেস তৈরি করার জন্য একটি 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
প্রদান করবে।
প্রথম ক্ষেত্রে পরীক্ষা করার জন্য এই পদক্ষেপগুলি অনুসরণ করুন:
- ফেরত পাঠানোর জন্য পোস্ট ডেটা সহ
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)); }
শেষ থেকে শেষ পরীক্ষা
ইউনিট পরীক্ষাগুলি নিশ্চিত করে যে পৃথক পেজিং উপাদানগুলি বিচ্ছিন্নভাবে কাজ করে, তবে শেষ থেকে শেষ পরীক্ষাগুলি আরও আত্মবিশ্বাস প্রদান করে যে অ্যাপ্লিকেশনটি সামগ্রিকভাবে কাজ করে। এই পরীক্ষাগুলির জন্য এখনও কিছু উপহাস নির্ভরতার প্রয়োজন হবে, তবে সাধারণত তারা আপনার অ্যাপ কোডের বেশিরভাগ কভার করবে।
এই বিভাগের উদাহরণটি পরীক্ষায় নেটওয়ার্ক ব্যবহার এড়াতে একটি মক API নির্ভরতা ব্যবহার করে। মক এপিআই কনফিগার করা হয়েছে টেস্ট ডেটার একটি সামঞ্জস্যপূর্ণ সেট ফেরানোর জন্য, যার ফলে পুনরাবৃত্তিযোগ্য পরীক্ষা হয়। প্রতিটি নির্ভরতা কী করে, এর আউটপুট কতটা সামঞ্জস্যপূর্ণ, এবং আপনার পরীক্ষা থেকে আপনার কতটা বিশ্বস্ততা প্রয়োজন তার উপর ভিত্তি করে মক বাস্তবায়নের জন্য কোন নির্ভরতাগুলিকে অদলবদল করতে হবে তা নির্ধারণ করুন।
আপনার কোড এমনভাবে লিখুন যা আপনাকে আপনার নির্ভরতাগুলির মক সংস্করণে সহজেই অদলবদল করতে দেয়। নিম্নলিখিত উদাহরণটি প্রয়োজন অনুসারে নির্ভরতা প্রদান এবং পরিবর্তন করতে একটি মৌলিক পরিষেবা লোকেটার বাস্তবায়ন ব্যবহার করে। বৃহত্তর অ্যাপগুলিতে, হিল্টের মতো নির্ভরতা ইনজেকশন লাইব্রেরি ব্যবহার করে আরও জটিল নির্ভরতা গ্রাফগুলি পরিচালনা করতে সহায়তা করতে পারে।
কোটলিন
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
আপডেট করে সঠিক সংখ্যক আইটেম সহ API থেকে প্রত্যাবর্তন করে যখন ব্যবহারকারী অনুসন্ধানের জন্য একটি ভিন্ন সাবরেডিট প্রবেশ করে।
কোটলিন
@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()); }); }
ইন্সট্রুমেন্টেড পরীক্ষাগুলি যাচাই করা উচিত যে ডেটা UI তে সঠিকভাবে প্রদর্শিত হয়। RecyclerView.Adapter
এ আইটেমগুলির সঠিক সংখ্যা বিদ্যমান কিনা তা যাচাই করে, অথবা পৃথক সারি ভিউগুলির মাধ্যমে পুনরাবৃত্তি করে এবং ডেটা সঠিকভাবে ফর্ম্যাট করা হয়েছে তা যাচাই করে এটি করুন৷
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট প্রদর্শিত হয়
- নেটওয়ার্ক এবং ডাটাবেস থেকে পৃষ্ঠা
- পেজিং 3 এ স্থানান্তর করুন
- পৃষ্ঠাযুক্ত ডেটা লোড এবং প্রদর্শন করুন