Odaklanarak gezinmeyi test etme

Odaklanma gezinme testlerini otomatikleştirmek, uygulamanızın donanım klavyeleri, D-pad'ler ve erişilebilirlik araçlarında tutarlı ve tahmin edilebilir bir kullanıcı deneyimi sunmasını sağlar. Compose, temel girişleri simüle etmek ve odak durumlarını doğrulamak için yerleşik test API'leri sağlar.

Odaklanma testleri oluşturma

Odak testleri yazmak için Compose testing kitaplığındaki createComposeRule() işlevini kullanın:

@RunWith(AndroidJUnit4::class)
class FocusNavigationTest {
    @get:Rule
    val composeTestRule = createComposeRule()

Odak hedeflerini doğrulama

Bir kullanıcı arayüzü öğesinin odaklanıp odaklanamayacağını tıklama işlemi yaparak veya odaklanma isteğinde bulunarak doğrulayabilir, ardından assertIsFocused() ve assertIsNotFocused() kullanarak durumunu onaylayabilirsiniz.

@Test
fun interactiveElement_isFocusTarget() {
    composeTestRule.setContent {
        AppTheme {
            CardListScreen()
        }
    }

    val firstCard = composeTestRule.onNodeWithTag("card_1")
    val secondCard = composeTestRule.onNodeWithTag("card_2")

    // Focus the first element
    firstCard.performClick()
    firstCard.assertIsFocused()
    secondCard.assertIsNotFocused()
}

Tek boyutlu odak geçişini test etme

Odağın kullanıcı arayüzünde görünüm sırasına göre ilerlediğini doğrulamak için performKeyInput kullanarak Sekme ve Üst Karakter+Sekme tuşlarına basma işlemini simüle edin. En iyi uygulama olarak, iki yönlü geçiş bütünlüğünü sağlamak için hem ileri gezinmeyi (Key.Tab) hem de ters gezinmeyi (Key.Tab ile Key.ShiftLeft basılı tutularak) doğrulayın:

@Test
fun tabKey_navigatesInAppearanceOrder() {
    composeTestRule.setContent {
        AppTheme {
            CardListScreen()
        }
    }

    val firstCard = composeTestRule.onNodeWithTag("card_1")
    val secondCard = composeTestRule.onNodeWithTag("card_2")
    val thirdCard = composeTestRule.onNodeWithTag("card_3")

    firstCard.performClick()
    firstCard.assertIsFocused()

    // Press Tab -> Moves to second card
    firstCard.performKeyInput {
        pressKey(Key.Tab)
    }
    secondCard.assertIsFocused()
    firstCard.assertIsNotFocused()

    // Press Tab -> Moves to third card
    secondCard.performKeyInput {
        pressKey(Key.Tab)
    }
    thirdCard.assertIsFocused()

    // Press Shift + Tab -> Moves backward to second card
    thirdCard.performKeyInput {
        withKeyDown(Key.ShiftLeft) {
            pressKey(Key.Tab)
        }
    }
    secondCard.assertIsFocused()
}

İki boyutlu yönlü geçişi test etme

Key.DirectionDown, Key.DirectionUp, Key.DirectionRight ve Key.DirectionLeft tuşlarını kullanarak ok tuşlarını veya D-pad yön tuşlarını simüle edin.

@Test
fun arrowKeys_moveFocusTwoDimensionallyWithoutWrap() {
    composeTestRule.setContent {
        AppTheme {
            GridLayoutScreen()
        }
    }

    val topLeftButton = composeTestRule.onNodeWithTag("btn_top_left")
    val bottomLeftButton = composeTestRule.onNodeWithTag("btn_bottom_left")

    topLeftButton.performClick()
    topLeftButton.assertIsFocused()

    // Down arrow moves focus downward to bottom-left button
    topLeftButton.performKeyInput {
        pressKey(Key.DirectionDown)
    }
    bottomLeftButton.assertIsFocused()

    // Directional keys do not wrap around: pressing Down on bottom element stays focused
    bottomLeftButton.performKeyInput {
        pressKey(Key.DirectionDown)
    }
    bottomLeftButton.assertIsFocused()
}

Metin alanı odak davranışını test etme

Tek satırlı metin alanlarının odağı Sekme tuşuna basıldığında ilerlettiğini, çok satırlı metin alanlarının ise odağı koruduğunu doğrulayın:

@Test
fun textField_handlesTabAccordingToLineLimits() {
    composeTestRule.setContent {
        AppTheme {
            FormScreen()
        }
    }

    val singleLineNode = composeTestRule.onNodeWithTag("single_line_field")
    val multiLineNode = composeTestRule.onNodeWithTag("multi_line_field")
    val submitButtonNode = composeTestRule.onNodeWithTag("submit_button")

    // Single-line field advances focus on Tab
    singleLineNode.performClick()
    singleLineNode.assertIsFocused()
    singleLineNode.performKeyInput { pressKey(Key.Tab) }
    multiLineNode.assertIsFocused()

    // Multi-line field keeps focus and inserts '\t'
    multiLineNode.performKeyInput { pressKey(Key.Tab) }
    multiLineNode.assertIsFocused()
    submitButtonNode.assertIsNotFocused()

    // Shift + Tab escapes multi-line field to previous target
    multiLineNode.performKeyInput {
        withKeyDown(Key.ShiftLeft) { pressKey(Key.Tab) }
    }
    singleLineNode.assertIsFocused()
}