কম্পোজ টেস্ট সাধারণত আপনার UI-এর সাথে সিঙ্ক করা থাকে। আপনি যখন ComposeTestRule-এর মাধ্যমে কোনও
অ্যাসার্শন বা অ্যাকশন কল করেন, তখন UI ট্রি আইডল না হওয়া পর্যন্ত অপেক্ষা করে
টেস্ট আগে থেকেই সিঙ্ক্রোনাইজ করা হয়।
সাধারণত, আপনাকে কোনও অ্যাকশন নিতে হয় না। তবে, কিছু ব্যতিক্রমী ঘটনা আছে যেগুলি আপনার জানা উচিত।
কোনও টেস্ট সিঙ্ক করা হলে, আপনার Compose অ্যাপ ভার্চুয়াল ঘড়ি ব্যবহার করে সময়ের সাথে সাথে উন্নত হয়। এর অর্থ হল, Compose টেস্ট রিয়েল টাইমে রান করে না, তাই সেগুলি যত দ্রুত সম্ভব পাস করতে পারে।
তবে, আপনি যদি আপনার টেস্ট সিঙ্ক্রোনাইজ করে এমন পদ্ধতি ব্যবহার না করেন, তাহলে কোনও রিকম্পোজিশন হবে না এবং UI পজ করা আছে বলে মনে হবে।
@Test
fun counterTest() {
val myCounter = mutableStateOf(0) // State that can cause recompositions.
var lastSeenValue = 0 // Used to track recompositions.
composeTestRule.setContent {
Text(myCounter.value.toString())
lastSeenValue = myCounter.value
}
myCounter.value = 1 // The state changes, but there is no recomposition.
// Fails because nothing triggered a recomposition.
assertTrue(lastSeenValue == 1)
// Passes because the assertion triggers recomposition.
composeTestRule.onNodeWithText("1").assertExists()
}মনে রাখবেন যে এই প্রয়োজনীয়তা শুধুমাত্র কম্পোজ হায়ারার্কির ক্ষেত্রে প্রযোজ্য এবং বাকি অ্যাপের ক্ষেত্রে নয়।
অটোমেটিক সিঙ্ক করার সুবিধা বন্ধ করুন
আপনি যখন ComposeTestRule-এর মাধ্যমে কোনও অ্যাসার্শন বা অ্যাকশন কল করেন, যেমন
assertExists(), আপনার টেস্ট Compose UI-এর সাথে সিঙ্ক করা হয়। কিছু ক্ষেত্রে
আপনি এই সিঙ্ক করা বন্ধ করে ঘড়িটি নিজেই নিয়ন্ত্রণ করতে চাইতে পারেন। যেমন, UI ব্যস্ত থাকার সময়ও অ্যানিমেশনের সঠিক স্ক্রিনশট নেওয়ার জন্য আপনি সময় কন্ট্রোল করতে পারেন। অটোমেটিক সিঙ্ক করার সুবিধা বন্ধ করতে,
mainClock-এ autoAdvance প্রপার্টি false-এ সেট করুন:
composeTestRule.mainClock.autoAdvance = false
সাধারণত, আপনি নিজেই সময় এগিয়ে দেবেন। আপনি advanceTimeByFrame()-এর মাধ্যমে ঠিক একটি ফ্রেম
বা advanceTimeBy()-এর মাধ্যমে নির্দিষ্ট সময়সীমা
এগিয়ে যেতে পারবেন:
composeTestRule.mainClock.advanceTimeByFrame()
composeTestRule.mainClock.advanceTimeBy(milliseconds)
নিষ্ক্রিয় রিসোর্স
Compose টেস্ট ও UI সিঙ্ক করতে পারে যাতে প্রতিটি অ্যাকশন ও অ্যাসারশন আইডল স্টেটে করা হয়, প্রয়োজন অনুযায়ী অপেক্ষা করে বা ঘড়ির সময় এগিয়ে দেয়। তবে, কিছু অ্যাসিঙ্ক্রোনাস অপারেশন যার ফলাফল UI-এর অবস্থাকে প্রভাবিত করে, সেগুলি ব্যাকগ্রাউন্ডে চালানো যেতে পারে, যদিও টেস্ট সেগুলির ব্যাপারে জানে না।
আপনার টেস্টে এইসব নিষ্ক্রিয় রিসোর্স তৈরি ও রেজিস্টার করুন যাতে টেস্ট করা অ্যাপটি ব্যস্ত নাকি নিষ্ক্রিয় তা নির্ধারণ করার সময় সেগুলি বিবেচনা করা হয়। আপনাকে কোনও অ্যাকশন নিতে হবে না, যদি না আপনাকে অতিরিক্ত নিষ্ক্রিয় রিসোর্স রেজিস্টার করতে হয়, যেমন, আপনি যদি এমন কোনও ব্যাকগ্রাউন্ড জব চালান যা Espresso বা Compose-এর সাথে সিঙ্ক করা নেই।
এই API, Espresso-এর নিষ্ক্রিয় রিসোর্স-এর মতোই, যা টেস্টের অধীনে থাকা সাবজেক্ট
নিষ্ক্রিয় নাকি ব্যস্ত তা নির্দেশ করে। IdlingResource-এর প্রয়োগ রেজিস্টার করতে
Compose টেস্ট রুল ব্যবহার করুন।
composeTestRule.registerIdlingResource(idlingResource)
composeTestRule.unregisterIdlingResource(idlingResource)
ম্যানুয়াল সিঙ্ক্রোনাইজেশন
কিছু ক্ষেত্রে, আপনার টেস্ট বা আপনি যে অ্যাপ টেস্ট করছেন তার অন্যান্য অংশের সাথে Compose UI সিঙ্ক করতে হবে।
waitForIdle() ফাংশনটি Compose-এর নিষ্ক্রিয় হওয়ার জন্য অপেক্ষা করে, কিন্তু ফাংশনটি
autoAdvance প্রপার্টির উপর নির্ভর করে:
composeTestRule.mainClock.autoAdvance = true // Default
composeTestRule.waitForIdle() // Advances the clock until Compose is idle.
composeTestRule.mainClock.autoAdvance = false
composeTestRule.waitForIdle() // Only waits for idling resources to become idle.
মনে রাখবেন, দুটি ক্ষেত্রেই, waitForIdle()-ও বাকি থাকা ড্র ও লেআউট
পাস-এর জন্য অপেক্ষা করে।
এছাড়াও, আপনি ঘড়ির সময় এগিয়ে নিয়ে যেতে পারেন যতক্ষণ না পর্যন্ত নির্দিষ্ট শর্ত পূরণ হচ্ছে
advanceTimeUntil()।
composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }
মনে রাখবেন যে প্রদত্ত শর্তটি এমন অবস্থা পরীক্ষা করা উচিত যা এই ঘড়ি দ্বারা প্রভাবিত হতে পারে (এটি শুধুমাত্র কম্পোজ স্টেট-এর সাথে কাজ করে)।
অ্যানিমেশন টেস্ট অপ্টিমাইজ করুন
হাই-ফিডেলিটি অ্যানিমেশন পরীক্ষা করার সময়, আপনাকে প্রায়ই অটো-অ্যাডভান্স বন্ধ করতে হয়
এবং ইন্টারমিডিয়েট UI স্টেট অ্যাসার্ট করার জন্য ম্যানুয়ালি ফ্রেমের মাধ্যমে স্টেপ করতে হয়। এইসব নির্দিষ্ট ফ্রেম-বাই-ফ্রেম লুপের জন্য, আপনার অ্যাসারশন এক্সিকিউট করতে runWithoutImplicitWait পদ্ধতি ব্যবহার করুন। স্ট্যান্ডার্ড নোড কোয়েরি (যেমন onNodeWithTag বা
fetchSemanticsNode) অন্তর্নিহিত সিঙ্ক্রোনাইজেশন ট্রিগার করে যা আপনি ম্যানুয়ালি ঘড়ি কন্ট্রোল করার সময়
অপ্রয়োজনীয় হয়ে পড়ে, তাই সেগুলি বাইপাস করলে আপনার টেস্ট রানটাইম
উল্লেখযোগ্যভাবে দ্রুত হয়।
ব্যবহার করার নির্দেশিকা
- ম্যানুয়াল ক্লক ম্যানেজমেন্ট: এই API ব্যবহার করুন যখন
mainClock.autoAdvancefalse-এ সেট করা থাকে এবং UI বর্তমান ফ্রেমের জন্য পরিচিত, স্টেবল অবস্থায় থাকে। - UI থ্রেড এক্সিকিউশন: UI ট্রির স্থায়িত্ব নিশ্চিত করতে, UI থ্রেডে
runWithoutImplicitWaitকল করুন, যেমনrunOnUiThread-এর সাথে। UI থ্রেড বন্ধ করে এটি চালালে আপনার টেস্ট রেস কন্ডিশন ও স্টেট রিড স্টেল হওয়ার মতো সমস্যার সম্মুখীন হতে পারে। - শুধুমাত্র-পড়ার অ্যাসার্শন: ব্লকে শুধুমাত্র-পড়ার অ্যাসার্শন থাকতে হবে। স্টেট মিউটেট করে এমন যেকোনও অ্যাকশন এই ব্লকের বাইরে সম্পূর্ণ করতে হবে।
উদাহরণ
@Test fun runWithoutImplicitWaitSample() = runComposeUiTest { setContent { MainScreen() } mainClock.autoAdvance = false // Trigger an animation onNodeWithText("Start Animation").performClick() // Step through the animation frame-by-frame while (hasPendingWork()) { mainClock.advanceTimeByFrame() waitForIdle() runOnUiThread { // Suppress implicit synchronization inside this block to avoid redundant // waits on each node query, making the frame assertions execute much faster. runWithoutImplicitWait { val box1 = onNodeWithTag("Box1").fetchSemanticsNode() val box2 = onNodeWithTag("Box2").fetchSemanticsNode() val box3 = onNodeWithTag("Box3").fetchSemanticsNode() // Assert the exact intermediate state of all three properties for this frame assert(box1.boundsInRoot.right <= box2.boundsInRoot.left) assert(box2.boundsInRoot.right <= box3.boundsInRoot.left) } } } }
মূল থ্রেড সিঙ্ক্রোনাইজেশন
কম্পোজ টেস্টিং এখন মূল থ্রেড সিঙ্ক্রোনাইজেশনকে সাপোর্ট করে, যার ফলে আপনি নিরাপদে
waitForIdle— এবং এক্সটেনশনের মাধ্যমে, কম্পোজ UI অ্যাকশন এবং
অ্যাসারশন — সরাসরি মূল থ্রেড থেকে কল করতে পারবেন।
আগে, Compose টেস্টিং কঠোরভাবে একটি টু-থ্রেড মডেল প্রয়োগ করত: টেস্ট এক্সিকিউশন
ব্যাকগ্রাউন্ড টেস্ট থ্রেডে ঘটত, যখন UI আপডেটগুলি প্রধান
থ্রেডে ঘটত। প্রধান থ্রেড থেকে (যেমন, runOnUiThread ব্লকের মধ্যে) waitForIdle বা runOnIdle-এর মতো সিঙ্ক্রোনাইজেশন পদ্ধতি কল করলে
IllegalStateException থ্রো করবে কারণ ফ্রেমওয়ার্ক প্রধান-থ্রেড সিঙ্ক্রোনাইজেশন প্রতিরোধ করতে
কঠোর থ্রেড চেক এনফোর্স করে।
প্রধান থ্রেড সিঙ্ক্রোনাইজেশন চালু থাকলে, Compose টেস্ট ফ্রেমওয়ার্ক এখন প্রধান থ্রেডে ব্লকিং কল করা হলেও ঘড়ি এবং প্রসেস পেন্ডিং কাজকে এগিয়ে নিয়ে যেতে পারে।
কখন মূল থ্রেড সিঙ্ক্রোনাইজেশন ব্যবহার করতে হবে
ব্যাকগ্রাউন্ড থ্রেডে টেস্ট করা পিওর Compose টেস্টের জন্য স্ট্যান্ডার্ড হলেও, কিছু নির্দিষ্ট পরিস্থিতিতে মূল থ্রেড সিঙ্ক্রোনাইজেশন অত্যন্ত সুবিধাজনক:
- জটিল ভিউ ইন্টার-অপারেবিলিটি: Compose এবং লিগ্যাসি Android ভিউ, দুটিই আছে এমন হাইব্রিড UI টেস্ট করার সময়, ভিউ ম্যানিপুলেট করার জন্য প্রায়ই মেন থ্রেডে রান করতে হয়। আপনি এখন ক্রমাগত থ্রেড প্রসঙ্গ পরিবর্তন না করেই ক্রমানুসারে ভিউয়ের সাথে ইন্টার্যাক্ট করতে এবং কম্পোজ নোডে অ্যাসার্ট করতে পারবেন।
- সিঙ্ক্রোনাস স্টেট মিউটেশন: আপনার আর্কিটেকচার যদি কঠোরভাবে মেন-থ্রেড-বাউন্ড স্টেট হোল্ডারের উপর নির্ভর করে, তাহলে আপনি এখন স্টেট মিউটেট করতে পারবেন এবং মেন থ্রেড না ছেড়েই Compose UI সেটেল হওয়ার জন্য অবিলম্বে অপেক্ষা করতে পারবেন।
- কাস্টম টেস্ট রানার: আপনি যদি কাস্টম টেস্টিং ইনফ্রাস্ট্রাকচার তৈরি করেন বা এমন এনভায়রনমেন্ট ব্যবহার করেন যেখানে টেস্ট রানার সহজাতভাবে মূল থ্রেডে রান করে, তাহলে Compose টেস্ট এখন ব্যাকগ্রাউন্ড-থ্রেড ডেলিগেশন ছাড়াই পরিষ্কারভাবে রান করে।
উদাহরণ
ঐতিহাসিকভাবে, প্রধান থ্রেডে সিঙ্ক্রোনাইজেশন কঠোরভাবে নিষিদ্ধ ছিল বলে, ডেভেলপারদের ব্যাকগ্রাউন্ড টেস্ট রানার থ্রেড এবং UI থ্রেডের মধ্যে বারবার পরিবর্তন করতে হত, যার ফলে টেস্টগুলি বিচ্ছিন্ন হয়ে যেত:
@Test fun testBidirectionalInteropUIUpdates_old() { val scenario = launchFragmentInContainer<InteropFragment>() composeTestRule.waitForIdle() scenario.onFragment { fragment -> fragment.legacyButton.performClick() } // Jump to Test Thread to verify state settles inside compose composeTestRule.waitForIdle() composeTestRule.onNodeWithText("Legacy Clicks: 1").assertIsDisplayed() composeTestRule.onNodeWithText("Increment Legacy TextView").performClick() composeTestRule.waitForIdle() // Jump back to Main Thread to verify target view state settles scenario.onFragment { fragment -> assert(fragment.legacyTextView.text.toString() == "Compose Clicks: 1") } }
মেন থ্রেড সিঙ্ক্রোনাইজেশন চালু থাকলে, Compose ও View হায়ারার্কির জন্য অ্যাসারশন একই ব্লকে এক্সিকিউট করা যেতে পারে:
@Test fun testBidirectionalInteropUIUpdates_new() { val scenario = launchFragmentInContainer<InteropFragment>() composeTestRule.waitForIdle() scenario.onFragment { fragment -> fragment.legacyButton.performClick() composeTestRule.waitForIdle() composeTestRule.onNodeWithText("Legacy Clicks: 1").assertIsDisplayed() composeTestRule.onNodeWithText("Increment Legacy TextView").performClick() composeTestRule.waitForIdle() assert(fragment.legacyTextView.text.toString() == "Compose Clicks: 1") } }
শর্তের জন্য অপেক্ষা করুন
ডেটা লোড করা বা Android-এর
পরিমাপ বা ড্র (অর্থাৎ, Compose-এর বাইরে পরিমাপ বা ড্র) করার মতো বাহ্যিক কাজের উপর নির্ভর করে এমন যেকোনও শর্তের ক্ষেত্রে
waitUntil()-এর মতো আরও সাধারণ ধারণা ব্যবহার করা উচিত:
composeTestRule.waitUntil(timeoutMs) { condition }
এছাড়াও, আপনি যেকোনও
waitUntil হেল্পার ব্যবহার করতে পারেন:
composeTestRule.waitUntilAtLeastOneExists(matcher, timeoutMs)
composeTestRule.waitUntilDoesNotExist(matcher, timeoutMs)
composeTestRule.waitUntilExactlyOneExists(matcher, timeoutMs)
composeTestRule.waitUntilNodeCount(matcher, count, timeoutMs)
অতিরিক্ত রিসোর্স
- Android-এ অ্যাপ টেস্ট করা: Android-এ টেস্ট করার মূল ল্যান্ডিং পৃষ্ঠা থেকে টেস্টিংয়ের মূল বিষয় ও টেকনিক সম্পর্কে আরও বিস্তারিত তথ্য পাওয়া যায়।
- পরীক্ষা করার মূল বিষয়: Android অ্যাপ পরীক্ষা করার পিছনে থাকা মূল ধারণা সম্পর্কে আরও জানুন।
- লোকাল টেস্ট: আপনি নিজের ওয়ার্কস্টেশনে লোকালি কিছু টেস্ট চালাতে পারবেন।
- ইনস্ট্রুমেন্টেড টেস্ট: ইনস্ট্রুমেন্টেড টেস্ট রান করানোও একটি ভালো অভ্যাস। অর্থাৎ, সরাসরি ডিভাইসে রান করা টেস্ট।
- কন্টিনিউয়াস ইন্টিগ্রেশন: কন্টিনিউয়াস ইন্টিগ্রেশন আপনাকে ডিপ্লয়মেন্ট পাইপলাইনে টেস্ট ইন্টিগ্রেট করতে দেয়।
- বিভিন্ন স্ক্রিন সাইজ টেস্ট করুন: ব্যবহারকারীদের কাছে অনেক ডিভাইস উপলভ্য, তাই আপনাকে বিভিন্ন স্ক্রিন সাইজের জন্য টেস্ট করতে হবে।
- Espresso: View-ভিত্তিক UI-এর জন্য তৈরি করা হলেও, Espresso-এর জ্ঞান Compose টেস্টিংয়ের কিছু ক্ষেত্রে সহায়ক হতে পারে।