フォーカス ナビゲーション テストを自動化することで、ハードウェア キーボード、D-pad、ユーザー補助ツールで一貫性のある予測可能なユーザー エクスペリエンスをアプリが提供できるようになります。Compose には、キー入力をシミュレートしてフォーカス状態を確認するための組み込みテスト API が用意されています。
フォーカス テストを設定する
フォーカス テストを作成するには、Compose テスト ライブラリの createComposeRule() を使用します。
@RunWith(AndroidJUnit4::class) class FocusNavigationTest { @get:Rule val composeTestRule = createComposeRule()
フォーカス ターゲットを確認する
UI 要素がフォーカスを受け取れるかどうかは、クリックを実行するかフォーカスをリクエストしてから、assertIsFocused() と assertIsNotFocused() を使用してその状態をアサートすることで確認できます。
@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() }
1 次元のフォーカス走査をテストする
performKeyInput を使用して Tab キーと Shift+Tab キーの押下をシミュレートし、フォーカスが外観の順序で UI を移動することを確認します。ベスト プラクティスとして、双方向のトラバーサル整合性を確保するために、順方向ナビゲーション(Key.Tab)と逆方向ナビゲーション(Key.ShiftLeft を押しながら Key.Tab)の両方を確認します。
@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() }
2 次元方向のトラバーサルをテストする
Key.DirectionDown、Key.DirectionUp、Key.DirectionRight、Key.DirectionLeft を使用して、矢印キーまたは D-pad の方向キーをシミュレートします。
@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() }
テキスト フィールドのフォーカス動作をテストする
単一行のテキスト フィールドでは Tab キーでフォーカスが移動し、複数行のテキスト フィールドではフォーカスが維持されることを確認します。
@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() }
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- フォーカス ターゲット
- フォーカス移動順序
- テキスト フィールドのフォーカス
- フォーカス ターゲットをグループ化する