סנכרון הבדיקות

בדיקות ה-Compose מסונכרנות כברירת מחדל עם ממשק המשתמש. כשקוראים לאימרה או לפעולה עם ComposeTestRule, הבדיקה מסתנכרנת מראש ומחכה עד שצירוף ה-UI יהיה במצב חוסר פעילות.

בדרך כלל אין צורך לבצע פעולה כלשהי. עם זאת, יש כמה מקרים קיצוניים שחשוב לדעת עליהם.

כשמפעילים סנכרון של בדיקה, אפליקציית Compose מתקדמת בזמן באמצעות שעון וירטואלי. המשמעות היא שבדיקות Compose לא פועלות בזמן אמת, כדי שהן יוכלו לעבור כמה שיותר מהר.

עם זאת, אם לא משתמשים בשיטות לסנכרון הבדיקות, לא תתבצע יצירת מחדש של הממשק המשתמש, והוא ייראה כמושהה.

@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()
}

חשוב לזכור שהדרישה הזו חלה רק על היררכיות של Compose ולא על שאר האפליקציה.

השבתת הסנכרון האוטומטי

כשאתם קוראים לטענת נכוֹנוּת או לפעולה דרך ComposeTestRule, כמו assertExists(), הבדיקה מסתנכרנת עם ממשק המשתמש של Compose. במקרים מסוימים, כדאי להפסיק את הסנכרון הזה ולשלוט בשעון בעצמכם. לדוגמה, תוכלו לקבוע מתי לצלם צילומי מסך מדויקים של אנימציה, בשלב שבו ממשק המשתמש עדיין יהיה פעיל. כדי להשבית את הסנכרון האוטומטי, מגדירים את המאפיין autoAdvance בקובץ mainClock לערך false:

composeTestRule.mainClock.autoAdvance = false

בדרך כלל תצטרכו להקדים את הזמן בעצמכם. אפשר להתקדם בדיוק תמונה אחת באמצעות advanceTimeByFrame() או למשך זמן מסוים באמצעות advanceTimeBy():

composeTestRule.mainClock.advanceTimeByFrame()
composeTestRule.mainClock.advanceTimeBy(milliseconds)

משאבים במצב המתנה

Compose יכול לסנכרן את הבדיקות ואת ממשק המשתמש, כך שכל פעולה וכל טענת נכוֹנוּת (assertion) יתבצעו במצב חוסר פעילות, בהמתנה או בהקדמת השעון לפי הצורך. עם זאת, חלק מהפעולות האסינכרוניות שהתוצאות שלהן משפיעות על מצב ממשק המשתמש יכולות לפעול ברקע, בלי שהבדיקה תהיה מודעת אליהן.

יוצרים את משאבי ההמתנה האלה ומירשמם בבדיקה, כדי שהם יילקחו בחשבון כשמגדירים אם האפליקציה שנבדקת עסוקה או לא. אין צורך לבצע פעולה כלשהי, אלא אם אתם צריכים לרשום משאבים נוספים במצב חוסר פעילות, למשל אם אתם מריצים משימה ברקע שלא מסונכרנת עם Espresso או Compose.

ה-API הזה דומה מאוד ל-Idling Resources של Espresso, ומציין אם הנושא שנבדק פעיל או לא פעיל. משתמשים בכללי הבדיקה Compose כדי לרשום את ההטמעה של IdlingResource.

composeTestRule.registerIdlingResource(idlingResource)
composeTestRule.unregisterIdlingResource(idlingResource)

סנכרון ידני

במקרים מסוימים, צריך לסנכרן את ממשק המשתמש של Compose עם חלקים אחרים של הבדיקה או עם האפליקציה שאתם בודקים.

הפונקציה 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 }

חשוב לזכור שהתנאי צריך לבדוק את המצב שיכול להיות מושפע מהשעון הזה (הוא פועל רק במצב 'כתיבה').

המתנה לתנאים

בכל תנאי שתלויות בעבודה חיצונית, כמו טעינת נתונים או מדידה או ציור ב-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)

מקורות מידע נוספים