以下为本文档的中文说明
该技能用于使用ADB在Android模拟器上测试Expo Router功能,涵盖安装APK、截图、坐标点击、滑动、键盘输入和文本提取等操作。主要功能是提供一套在Android模拟器上手动测试Expo Router屏幕和组件的端到端测试方法。使用场景包括:在实现或修改原生Android UI组件后(工具栏、标签页、菜单);在验证Jetpack Compose组件时;在运行native-navigation或其他E2E应用的Android版本时;在提交影响Android特定行为的PR之前。前置条件包括:Android模拟器必须正在运行;推荐使用Pixel模拟器而非平板模拟器进行标准手机尺寸测试。典型测试步骤包括:验证模拟器连接状态;检查模拟器屏幕分辨率(对坐标计算很重要);构建并安装测试APK;执行截图、点击、滑动等交互操作并验证结果。该技能提供了一套轻量级的Android E2E测试方案,适合在开发过程中快速验证UI行为。
Android E2E Testing for Expo Router
Useadbto manually test Expo Router screens and components on Android emulators.
When to Use
- After implementing or modifying native Android UI components (toolbars, tabs, menus)
- When verifying Jetpack Compose components (
@expo/ui/jetpack-compose) - When running the
native-navigationor other E2E apps on Android - Before opening a PR that touches Android-specific behavior
Prerequisites
An Android emulator must be running. PreferPixel emulatorsover tablet ones for standard phone-sized testing.
# Verify emulator is connectedadb devices# Check which emulator is runningadb-sDEVICE_ID emu avd name# Check screen resolution (important for coordinate calculations)adb-sDEVICE_ID shell wm sizeStep 1: Build and Launch
Router E2E apps
Package name:dev.expo.routere2e
Each app inapps/router-e2e/__e2e__/has a correspondingyarn android:<name>script. Checkapps/router-e2e/package.jsonfor available scripts.
cdapps/router-e2eyarnandroid:[APP_NAME]# e.g. yarn android:native-navigationIf the app is already built, relaunch it:
adb shell monkey-pdev.expo.routere2e-candroid.intent.category.LAUNCHER1To find the package name of any installed app:
adb shell pm list packages|grep-i<keyword>Step 2: Navigate Using UI Dump
CRITICAL: Always useuiautomator dumpfor element coordinates.Screenshot pixel coordinates have display scaling factors that make them unreliable foradb shell input tap. The UI dump provides actual device coordinates.
Dump the view hierarchy
adb shell uiautomator dump /sdcard/ui.xml&&adb shellcat/sdcard/ui.xmlThis returns XML with every UI element including:
text— displayed textcontent-desc— accessibility description (useful for icon buttons)bounds— position as[left,top][right,bottom]clickable— whether the element responds to tapsclass— Android view class
Find and tap an element
- Search the XML for your target element by
textorcontent-desc - Extract the
boundsattribute:bounds="[left,top][right,bottom]" - Calculate center:
x = (left + right) / 2,y = (top + bottom) / 2 - Tap:
adb shell input tap<x><y>Example:Forbounds="[367,498][714,633]":
- x = (367 + 714) / 2 = 540
- y = (498 + 633) / 2 = 565
adb shell input tap540565Wait for navigation to settle
After tapping a navigation element, wait before verifying:
sleep1For slow transitions or heavy screens, usesleep 2.
Step 3: Interact
Tap items
adb shell input tap<x><y>Scroll
# Scroll downadb shell input swipe5401500540500300# Scroll upadb shell input swipe5405005401500300# Scroll further (larger distance)adb shell input swipe5401500540200500Type text
adb shell input text"hello%sworld"# %s = spacePress hardware buttons
adb shell input keyevent4# Backadb shell input keyevent3# Homeadb shell input keyevent82# Menu / React Native dev menuLong press
adb shell input swipe<x><y><x><y>1000Step 4: Verify
Visual verification via screenshot
adb shell screencap-p/sdcard/screenshot.png&&adb pull /sdcard/screenshot.png /tmp/screenshot.pngThen use theReadtool to view/tmp/screenshot.png. Screenshots are useful for:
- Confirming visual appearance (colors, layout, styling)
- Verifying toolbar/tab positioning
- Checking selection states and visual feedback
Note:Use screenshots forvisualverification only. For element positions and tapping, always useuiautomator dump.
Programmatic verification via UI dump
adb shell uiautomator dump /sdcard/ui.xml&&adb shellcat/sdcard/ui.xmlSearch the XML for expected content:
- Verify text content appears
- Check
content-descfor accessibility labels - Confirm element presence after navigation
- Verify selection sta
tes (look for checkmarks, content-desc changes)
Check for errors
# React Native JS errorsadb logcat-d-sReactNativeJS:E|tail-20# Crash logsadb logcat-bcrash-d# All recent errorsadb logcat-d*:E|tail-30Step 5: Report Results
After testing, summarize results in a table:
| Test | Result |
|---|---|
| Navigation to screen | PASS/FAIL |
| Component renders correctly | PASS/FAIL |
| Interaction works | PASS/FAIL |
| No JS errors in logcat | PASS/FAIL |
Include details for any failures: what was expected vs what happened, relevant logcat output, and screenshots.
Preferably attach screenshots for features you tested.
Testing Jetpack Compose Components
Components from@expo/ui/jetpack-compose(likeHorizontalFloatingToolbar,IconButton,Host) render as Compose views inside React Native. In UI dumps they appear as:
androidx.compose.ui.platform.ComposeView— the Compose containerandroid.widget.HorizontalScrollView— inside toolbar layoutsandroid.widget.Button— Compose buttonsandroid.view.Viewwithcontent-desc— icon buttons with accessibility labels
When testing Compose components:
- Look for
content-descattributes to identify buttons (e.g.,content-desc="Clear selection") - The
ComposeViewwrapper may have different bounds than the inner interactive elements - Tap the interactive element’s bounds, not the container’s
Troubleshooting
uiautomator dump fails or returns empty
This can happen during animations or transitions. Wait and retry:
sleep2&&adb shell uiautomator dump /sdcard/ui.xml&&adb shellcat/sdcard/ui.xmlTap doesn’t register
- Recalculate coordinates from a fresh UI dump — the layout may have shifted
- Ensure you’re tapping a
clickable="true"element - Try tapping the parent element if the child isn’t clickable
App navigated to wrong screen or went to home
- The Back button (
keyevent 4) can exit the app entirely if on the root screen - Use
monkeycommand to relaunch:adb shell monkey -p dev.expo.routere2e -c android.intent.category.LAUNCHER 1 - Wait 2 seconds after launch before interacting
Metro bundler not connecting
adb reverse tcp:8081 tcp:8081App crashes on launch
# Check crash bufferadb logcat-bcrash-d# Look for fatal exceptionsadb logcat-d|grep-A10"FATAL EXCEPTION"Reload the app
# Open React Native dev menu and tap Reloadadb shell input keyevent82# Or force-stop and relaunchadb shell am force-stop dev.expo.routere2e&&adb shell monkey-pdev.expo.routere2e-candroid.intent.category.LAUNCHER1Disable Animations (Recommended for Testing)
Disabling animations prevents flaky UI dumps and makes testing more reliable:
adb shell settings put global window_animation_scale0adb shell settings put global transition_animation_scale0adb shell settings put global animator_duration_scale0Re-enable when done:
adb shell settings put global window_animation_scale1adb shell settings put global transition_animation_scale1adb shell settings put global animator_duration_scale1Complete Example: Testing a Toolbar Screen
# 1. Launch appadb shell monkey-pdev.expo.routere2e-candroid.intent.category.LAUNCHER1sleep2# 2. Dump UI to find navigation buttonadb shell uiautomator dump /sdcard/ui.xml&&adb shellcat/sdcard/ui.xml# Find: content-desc="Android Toolbar" bounds="[367,498][714,633]"# Center: (540, 565)# 3. Navigate to screenadb shell input tap540565sleep1# 4. Take screenshot to verify visual appearanceadb shell screencap-p/sdcard/screenshot.png&&adb pull /sdcard/screenshot.png /tmp/screenshot.png# 5. Dump UI to find toolbar buttonsadb shell uiautomator dump /sdcard/ui.xml&&adb shellcat/sdcard/ui.xml# Find buttons by content-desc: "Clear selection", "Select all", "Delete", "Add"# 6. Test toolbar interactionsadb shell input tap4572233# "Select all" button centersleep1# 7. Verify state changedadb shell screencap-p/sdcard/screenshot.png&&adb pull /sdcard/screenshot.png /tmp/screenshot.png# 8. Check for errorsadb logcat-d-sReactNativeJS:E|tail-20