يوفّر Compose واجهة برمجة التطبيقات ComposeTestRule التي تتيح لك كتابة اختبارات للصور المتحركة بطريقة حتمية مع التحكّم الكامل في ساعة الاختبار. يتيح لك ذلك التحقق من قيم الرسوم المتحركة المتوسطة. بالإضافة إلى ذلك، يمكن إجراء الاختبار بشكل أسرع
من المدة الفعلية للرسوم المتحركة.
يعرض "ComposeTestRule" ساعة الاختبار على أنّها "mainClock". يمكنك ضبط السمة autoAdvance على "خطأ" للتحكّم في الساعة في رمز الاختبار. بعد
بدء الحركة التي تريد اختبارها، يمكن تقديم الساعة باستخدام
advanceTimeBy.
يُرجى العِلم أنّ advanceTimeBy لا يغيّر الوقت بالمدة المحدّدة بالضبط. بدلاً من ذلك، يتم تقريبها إلى أقرب مدة تكون
مضاعفًا لمدة اللقطة.
@get:Rule val rule = createComposeRule() @Test fun testAnimationWithClock() { // Pause animations rule.mainClock.autoAdvance = false var enabled by mutableStateOf(false) rule.setContent { val color by animateColorAsState( targetValue = if (enabled) Color.Red else Color.Green, animationSpec = tween(durationMillis = 250) ) Box(Modifier.size(64.dp).background(color)) } // Initiate the animation. enabled = true // Let the animation proceed. rule.mainClock.advanceTimeBy(50L) // Compare the result with the image showing the expected result. // `assertAgainGolden` needs to be implemented in your code. rule.onRoot().captureToImage().assertAgainstGolden() }
تحسين اختبارات الصور المتحركة
عند اختبار الصور المتحركة العالية الدقة، غالبًا ما تحتاج إلى إيقاف ميزة "الانتقال التلقائي" والتنقّل يدويًا بين اللقطات لتأكيد حالات واجهة المستخدم المتوسطة. بالنسبة إلى هذه التكرارات الحلقية المحدّدة لكل لقطة، استخدِم طريقة runWithoutImplicitWait لتنفيذ التأكيدات. تؤدي طلبات البحث العادية عن العُقد (مثل onNodeWithTag أو
fetchSemanticsNode) إلى عمليات مزامنة ضمنية زائدة عن الحاجة
عند التحكّم في الساعة يدويًا، لذا فإنّ تجاوزها يؤدي إلى تسريع
أوقات تشغيل الاختبار بشكل كبير.
إرشادات الاستخدام
- إدارة الساعة يدويًا: استخدِم واجهة برمجة التطبيقات هذه عندما يتم ضبط
mainClock.autoAdvanceعلىfalseوتكون واجهة المستخدم في حالة معروفة ومستقرة للإطار الحالي. - تنفيذ سلسلة واجهة المستخدم: لضمان استقرار شجرة واجهة المستخدم، يمكنك استدعاء
runWithoutImplicitWaitفي سلسلة واجهة المستخدم، مثلrunOnUiThread. ويؤدي تشغيلها خارج سلسلة واجهة المستخدم إلى تعرُّض الاختبار لحالات السباق وقراءات الحالة القديمة. - تأكيدات للقراءة فقط: يجب أن يحتوي المقتطف على تأكيدات للقراءة فقط. يجب تنفيذ أي إجراءات تغيّر الحالة خارج هذه الكتلة.
مثال
@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) } } } }
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- اختبار تنسيق Compose
- اعتبارات أخرى
- تخصيص الصور المتحركة