করুটিন এক্সিকিউশনের উপর নিয়ন্ত্রণ উন্নত করতে Compose টেস্টিং API-এর v2 ভার্সন (createComposeRule,
createAndroidComposeRule, runComposeUiTest,
runAndroidComposeUiTest, ইত্যাদি) এখন উপলভ্য। এই আপডেট সম্পূর্ণ API সারফেস ডুপ্লিকেট করে না;
শুধুমাত্র টেস্ট এনভায়রনমেন্ট তৈরি করে এমন API আপডেট করা হয়েছে।
v1 API বন্ধ করা হয়েছে এবং নতুন API-তে মাইগ্রেট করার জন্য বিশেষভাবে সাজেস্ট করা হচ্ছে। মাইগ্রেট করলে, আপনার টেস্ট স্ট্যান্ডার্ড কোরুটিন আচরণের সাথে সামঞ্জস্যপূর্ণ কিনা তা যাচাই করা হয় এবং ভবিষ্যতে মানানসই সংক্রান্ত সমস্যা এড়ানো যায়। বন্ধ হয়ে যাওয়া v1 API-এর তালিকা দেখতে, API ম্যাপিং দেখুন।
v1 API, UnconfinedTestDispatcher-এর উপর নির্ভর করলেও, v2 API, রান করা কম্পোজিশনের জন্য ডিফল্ট হিসেবে
StandardTestDispatcher ব্যবহার করে। এই পরিবর্তন
স্ট্যান্ডার্ড runTest API-এর সাথে Compose টেস্টের আচরণকে সারিবদ্ধ করে এবং
কোরুটিন এক্সিকিউশন অর্ডারের উপর স্পষ্ট নিয়ন্ত্রণ প্রদান করে।
টেস্ট এনভায়রনমেন্ট কনফিগার করা
Compose test v2 API, টেস্ট এনভায়রনমেন্ট কাস্টমাইজ করতে ComposeUiTestConfig ব্যবহার করে। যেসব API টেস্টের জন্য সেট-আপ ফাংশন তৈরি করে, যেমন
createComposeRule, runComposeUiTest এবং অন্যান্য সম্পর্কিত API,
ComposeUiTestConfig গ্রহণ করে। এই কনফিগারেশন অবজেক্ট, পরিবেশ সংক্রান্ত API, যেমন effectContext, runTestContext
এবং testTimeout-কে একটি অবজেক্টে
একত্রিত করে।
কনফিগারেশন মডেল inputMode-ও ম্যানেজ করে। Compose test v2 APIs
প্রতিটি টেস্টের শুরুতে ডিফল্ট হিসেবে InputMode.Touch এনফোর্স করুন যাতে
নির্ধারক নিশ্চিত করা যায় এবং টেস্টের মধ্যে ইনপুট মোড স্টেট লিক হওয়া আটকানো যায়।
ComposeUiTestConfig হল Compose টেস্টিং v2 API-এর অংশ, যা ডিফল্ট হিসেবে
StandardTestDispatcher ব্যবহার করে। আপনার টেস্ট v1 API ব্যবহার করলে,
ComposeUiTestConfig গ্রহণ করার আগে
v2 টেস্টিং API-তে মাইগ্রেট করুন দেখুন।
ComposeUiTestConfig-এ মাইগ্রেট করুন
পরীক্ষায় সেট-আপ ফাংশন তৈরি করার জন্য ওভারলোডের মধ্যে, একাধিক ওভারলোড
যা স্বতন্ত্র কনফিগারেশন প্যারামিটার গ্রহণ করে -- যেমন effectContext,
runTestContext, বা testTimeout -- তা বন্ধ করা হয়েছে। পরিবর্তে ComposeUiTestConfig ব্যবহার করতে আপনার টেস্ট আপডেট করুন, যেমন নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
val testConfig = ComposeUiTestConfig( effectContext = EmptyCoroutineContext, runTestContext = EmptyCoroutineContext, testTimeout = 30.seconds ) @get:Rule val rule = createComposeRule(config = testConfig) // OR runComposeUiTest(config = testConfig) {}
ডিফল্ট ইনপুট মোড
মাইগ্রেশনের সময় পরীক্ষা ব্যর্থ হতে পারে যদি সেগুলি পরীক্ষা শুরু হওয়ার আগে
ইনস্ট্রুমেন্টেশন API-এর মাধ্যমে কনফিগার করা নন-টাচ ইনপুট মোডের উপর নির্ভর করে। টেস্টের জন্য সেট-আপ ফাংশনের মধ্যে, সিস্টেম ডিফল্ট হিসেবে প্রতিটি টেস্টের শুরুতে InputMode.Touch এনফোর্স করে যাতে আরও বেশি নির্ধারক হয় এবং স্টেট লিকেজ আটকানো যায়, অ্যাম্বিয়েন্ট ডিভাইস স্টেট এবং প্রি-টেস্ট সেট-আপ ওভাররাইড করে।
এই সমস্যার সমাধান করতে, ComposeUiTestConfig-এ প্রয়োজনীয় ইনপুট মোড উল্লেখ করুন:
class FocusTest { @get:Rule val rule = createComposeRule( config = ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) @Test fun testFocus() {} }
সম্পূর্ণ টেস্ট ক্লাসের পরিবর্তে স্বতন্ত্র টেস্ট কেসের জন্য ইনপুট মোড কনফিগার করতে, ComposeUiTestConfig থেকে runComposeUiTest-এ পাস করুন:
class FocusTest { @Test fun testTouchMode() = runComposeUiTest { // Runs with the default InputMode.Touch } @Test fun testKeyboardMode() = runComposeUiTest( ComposeUiTestConfig(inputMode = InputMode.Keyboard) ) { // Runs with InputMode.Keyboard } }
মাইগ্রেশন সংক্রান্ত অন্যান্য সমস্যা ও তার সমাধানের জন্য, সাধারণ সমস্যা ও তার সমাধান দেখুন।
v2 টেস্টিং API-তে মাইগ্রেট করুন
v2 API-তে আপগ্রেড করার সময়, আপনি সাধারণত খুঁজুন + বদল করুন বিকল্প ব্যবহার করে প্যাকেজ ইমপোর্ট আপডেট করতে এবং নতুন ডিসপ্যাচার পরিবর্তন গ্রহণ করতে পারবেন।
বিকল্প হিসেবে, নিম্নলিখিত প্রম্পট ব্যবহার করে Compose টেস্টিং API-এর v2-তে মাইগ্রেশন করার জন্য Gemini-কে বলুন:
AI প্রম্পট
v1 টেস্টিং API থেকে v2 টেস্টিং API-তে মাইগ্রেট করা
এই প্রম্পট v2 টেস্টিং API-তে মাইগ্রেট করার জন্য এই গাইড ব্যবহার করবে।
Migrate to Compose testing v2 APIs using the official
migration guide.v1 API-এর ব্যবহার বন্ধ করে v2 API-এর মাধ্যমে তা প্রতিস্থাপন করতে নিচের টেবিল ব্যবহার করুন:
বন্ধ করে দেওয়া হয়েছে (v1) |
রিপ্লেসমেন্ট (v2) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ব্যাকওয়ার্ড কম্প্যাটিবিলিটি ও ব্যতিক্রম
আগে থেকে থাকা v1 API এখন বন্ধ করে দেওয়া হয়েছে, তবে আগে থেকে থাকা আচরণ বজায় রাখতে এবং ব্রেকিং পরিবর্তন
আটকাতে
UnconfinedTestDispatcher ব্যবহার করা চালিয়ে যান।
শুধুমাত্র নিম্নলিখিত ক্ষেত্রে ডিফল্ট অ্যাকশন পরিবর্তন করা হয়েছে:
AndroidComposeUiTestEnvironment ক্লাসে কম্পোজিশন চালানোর জন্য ব্যবহৃত ডিফল্ট টেস্ট ডিসপ্যাচার
UnconfinedTestDispatcher থেকে StandardTestDispatcher-এ পাল্টে গেছে। এটি সেইসব ক্ষেত্রে প্রভাব ফেলে যেখানে
আপনি কনস্ট্রাক্টর বা সাবক্লাস
AndroidComposeUiTestEnvironment ব্যবহার করে ইনস্ট্যান্স তৈরি করেন এবং সেই কনস্ট্রাক্টরকে কল করেন।
মূল পরিবর্তন: Coroutine এক্সিকিউশনের উপর প্রভাব
API-এর v1 এবং v2-এর মধ্যে প্রাথমিক পার্থক্য হল কীভাবে কোরোটিনগুলি প্রেরণ করা হয়:
- v1 API (
UnconfinedTestDispatcher): কোনও কোরুটিন লঞ্চ করা হলে, এটি বর্তমান থ্রেডে অবিলম্বে কার্যকর করা হয়, প্রায়শই টেস্ট কোডের পরবর্তী লাইন চালানোর আগেই শেষ হয়ে যায়। প্রোডাকশন বিহেভিয়ারের মতো নয়, এই তাৎক্ষণিক এক্সিকিউশন অনিচ্ছাকৃতভাবে আসল টাইমিং সংক্রান্ত সমস্যা বা রেস কন্ডিশন মাস্ক করতে পারে যা লাইভ অ্যাপ্লিকেশনে ঘটবে। - v2 API (
StandardTestDispatcher): কোনও কোরুটিন লঞ্চ করা হলে, সেটি কিউতে থাকে এবং টেস্ট স্পষ্টভাবে ভার্চুয়াল ক্লক অ্যাডভান্স না করা পর্যন্ত এক্সিকিউট হয় না। স্ট্যান্ডার্ড Compose টেস্ট API (যেমনwaitForIdle()) আগে থেকেই এই সিঙ্ক্রোনাইজেশন ম্যানেজ করে, তাই এইসব স্ট্যান্ডার্ড API-এর উপর নির্ভর করে থাকা বেশিরভাগ টেস্ট কোনও পরিবর্তন ছাড়াই কাজ করা চালিয়ে যেতে পারবে।
সাধারণ সমস্যা এবং কীভাবে তা সমাধান করবেন
v2-এ আপগ্রেড করার পরে আপনার টেস্ট ব্যর্থ হলে, সম্ভবত নিম্নলিখিত প্যাটার্ন দেখা যাবে:
- ব্যর্থতা: আপনি কোনও টাস্ক লঞ্চ করেন (যেমন, ViewModel ডেটা লোড করে), কিন্তু আপনার অ্যাসার্শন সাথে সাথে ব্যর্থ হয় কারণ ডেটা এখনও "লোড হচ্ছে" স্ট্যাটাসে আছে।
- কারণ: v2 API-এর ক্ষেত্রে, কোরুটিন অবিলম্বে কার্যকর করার পরিবর্তে সারিবদ্ধ করা হয়। টাস্কটি সারিতে ছিল কিন্তু ফলাফল চেক করার আগে কখনও রান করেনি।
- সমাধান: সময় স্পষ্টভাবে এগিয়ে দিন। আপনাকে v2 ডিসপ্যাচারকে স্পষ্টভাবে বলতে হবে কখন কাজ করতে হবে।
আগের পদ্ধতি
v1-এ, টাস্কটি চালু হয়ে সাথে সাথেই শেষ হয়ে যায়। v2-তে, নিম্নলিখিত কোড
কাজ করে না কারণ loadData() এখনও রান করেনি।
// In v1, this launched and finished immediately.
viewModel.loadData()
// In v2, this fails because loadData() hasn't actually run yet!
assertEquals(Success, viewModel.state.value)
সাজেস্ট করা পদ্ধতি
অ্যাসার্ট করার আগে সারিবদ্ধ টাস্ক এক্সিকিউট করতে waitForIdle বা runOnIdle ব্যবহার করুন।
বিকল্প ১: UI নিষ্ক্রিয় না হওয়া পর্যন্ত waitForIdle ব্যবহার করে ঘড়ির সময় এগিয়ে নিয়ে যাওয়া,
করুটিন রান করেছে কিনা তা যাচাই করা।
viewModel.loadData()
// Explicitly run all queued tasks
composeTestRule.waitForIdle()
assertEquals(Success, viewModel.state.value)
বিকল্প ২: runOnIdle ব্যবহার করলে UI নিষ্ক্রিয় হয়ে যাওয়ার পরে UI থ্রেডে কোড ব্লক এক্সিকিউট হয়।
viewModel.loadData()
// Run the assertion after the UI is idle
composeTestRule.runOnIdle {
assertEquals(Success, viewModel.state.value)
}
ম্যানুয়াল সিঙ্ক্রোনাইজেশন
ম্যানুয়াল সিঙ্ক করার মতো পরিস্থিতিতে, যেমন অটো-অ্যাডভান্স বন্ধ থাকলে, কোরুটিন লঞ্চ করলে তা সাথে সাথে এক্সিকিউট হয় না কারণ
টেস্ট ক্লক পজ করা থাকে। ভার্চুয়াল ঘড়িকে এগিয়ে না নিয়ে গিয়ে কিউতে থাকা কোরুটিন এক্সিকিউট করতে, runCurrent() API ব্যবহার করুন। এটি বর্তমান ভার্চুয়াল সময়ের জন্য
শিডিউল করা টাস্ক রান করে।
composeTestRule.mainClock.scheduler.runCurrent()
waitForIdle()-এর বিপরীতে, যা UI স্থিতিশীল না হওয়া পর্যন্ত টেস্ট ক্লক এগিয়ে নিয়ে যায়, runCurrent() বর্তমান ভার্চুয়াল সময় বজায় রেখে মুলতুবি থাকা টাস্কগুলি সম্পাদন করে। এই আচরণটি মধ্যবর্তী অবস্থার যাচাইকরণ সক্ষম করে যা
ঘড়িটি নিষ্ক্রিয় অবস্থায় অগ্রসর হলে অন্যথায় এড়িয়ে যাওয়া হবে।
টেস্ট এনভায়রনমেন্টে ব্যবহৃত অন্তর্নিহিত টেস্ট শিডিউলার প্রকাশ করা হয়েছে। এই
শিডিউলারটি Kotlin runTest API-এর সাথে ব্যবহার করা যেতে পারে যাতে
টেস্ট ক্লক সিঙ্ক করা যায়।
runComposeUiTest-এ মাইগ্রেট করুন
আপনি যদি Kotlin runTest API-এর পাশাপাশি Compose টেস্ট API ব্যবহার করেন,
তাহলে runComposeUiTest-এ পরিবর্তন করার জন্য বিশেষভাবে সাজেস্ট করা হচ্ছে।
আগের পদ্ধতি
runTest-এর সাথে createComposeRule ব্যবহার করলে দুটি আলাদা ঘড়ি তৈরি হয়: একটি Compose-এর জন্য এবং অন্যটি টেস্ট coroutine স্কোপের জন্য। এই
কনফিগারেশন আপনাকে টেস্ট শিডিউলার ম্যানুয়ালি সিঙ্ক্রোনাইজ করতে বাধ্য করতে পারে।
@get:Rule val composeTestRule = createComposeRule() @Test fun testWithCoroutines() { composeTestRule.setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } // NOT RECOMMENDED // Fails: runTest creates a new, separate scheduler. // Advancing time here does NOT advance the compose clock. // To fix this without migrating, you would need to share the scheduler // by passing 'composeTestRule.mainClock.scheduler' to runTest. runTest { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() advanceTimeBy(1000) composeTestRule.onNodeWithText("Done!").assertIsDisplayed() } }
সাজেস্ট করা পদ্ধতি
runComposeUiTest API অটোমেটিক আপনার টেস্ট ব্লক নিজের
runTest স্কোপের মধ্যে এক্সিকিউট করে। টেস্ট ক্লক Compose এনভায়রনমেন্টের সাথে সিঙ্ক করা থাকে, তাই
আপনাকে আর ম্যানুয়ালি শিডিউলার ম্যানেজ করতে হয় না।
@Test fun testWithCoroutines() = runComposeUiTest { setContent { var status by remember { mutableStateOf("Loading...") } LaunchedEffect(Unit) { delay(1000) status = "Done!" } Text(text = status) } onNodeWithText("Loading...").assertIsDisplayed() mainClock.advanceTimeBy(1000 + 16 /* Frame buffer */) onNodeWithText("Done!").assertIsDisplayed() } }