UI Automator 相關介紹:html
在編寫測試代碼前,先確保如下兩個配置:
一、測試代碼存放位置
二、項目依賴(https://developer.android.com/training/testing/set-up-project)android
(1) 添加 Gradle 依賴(Add Gradle dependencies)git
allprojects { repositories { jcenter() google() } }
dependencies { ... androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' }
android { ... // Gradle automatically adds 'android.test.runner' as a dependency. useLibrary 'android.test.runner' useLibrary 'android.test.base' useLibrary 'android.test.mock' }
(2) 添加 manifest 聲明(Add manifest declarations)
此步驟可選,具體請看 https://developer.android.com/training/testing/set-up-project#add-manifest-declarationsgithub
當前面的配置完成後,進行其餘配置:
app下的build.gralde:編程
dependencies { ... androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' }
當全部配置都完成後,進行被測應用的 UI 組件分析,確保能被識別以及接入控制。微信
uiautomatorviewer:app
(1) 啓動手機上的被測應用框架
(2) 手機鏈接電腦ide
(3) 打開 Terminal, 進入目錄 <android-sdk>/tools/佈局
(4) 運行:uiautomatorviewer
查看應用的用戶界面屬性:
(1) 點擊左上角 "Device Screenshot" 按鈕
(2) 左邊是 UI 組件,右下半部分是屬性,右上半部分是佈局層級
(3) 可選功能:點擊右上角 "Toggle NAF Nodes" 按鈕(黃色三角形,內有感嘆號),查看沒法被識別/訪問的UI組件。---這個功能我都沒搞懂怎麼用,點擊後貌似沒效果
Android 原生元素具備更好的訪問性,利於測試代碼的編寫,無需額外的支持
若是是自定義 UI 元素,須要(1)建立一個繼承自 ExploreByTouchHelper 的實體類(2)經過調用 setAccessibilityDelegate() 將新建立的類的實例和特定的自定義 UI 元素相關聯
給自定義視圖元素添加無障礙功能的其餘參考資料:https://developer.android.com/guide/topics/ui/accessibility/custom-views.html
學習資料 for 提升 Android 的無障礙性/可訪問性:https://developer.android.com/guide/topics/ui/accessibility/apps.html
UI Automator 測試類的寫法和 JUnit 4 測試類的寫法是同樣的。
JUnit 4 測試類的學習資料:https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html#build
在測試類開頭添加註解:@RunWith(AndroidJUnit4.class)
同時,明確 AndroidX Test 中的 AndroidJUnitRunner 類爲默認的測試運行器。這個步驟的詳細描述:https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html#run
在 UI Automator 測試類中執行如下編程模型:
UiDevice: 接入和控制設備狀態的首要方法,可執行設備級別的行爲,例如改變屏幕旋轉方向、按下硬件按鈕、以及點擊 home 和 menu 鍵。
從設備的主屏幕開始測試是一個好的實踐。在主屏幕(或者其餘你在設備上選定的開始位置),能夠調用 UI Automator API 提供的方法和指定的 UI 元素進行交互。
如下代碼片斷展現瞭如何獲取一個 UiDevice 的實例以及模擬按下 home 鍵的操做:
import org.junit.Before; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.By; import androidx.test.uiautomator.Until; ... @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 18) public class ChangeTextBehaviorTest { private static final String BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"; private static final int LAUNCH_TIMEOUT = 5000; private static final String STRING_TO_BE_TYPED = "UiAutomator"; private UiDevice device; @Before public void startMainActivityFromHomeScreen() { // Initialize UiDevice instance device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen device.pressHome(); // Wait for launcher final String launcherPackage = device.getLauncherPackageName(); assertThat(launcherPackage, notNullValue()); device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); // Launch the app Context context = ApplicationProvider.getApplicationContext(); final Intent intent = context.getPackageManager() .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE); // Clear out any previous instances intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // Wait for the app to appear device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT); } }
示例代碼中的聲明:@SdkSuppress(minSdkVersion = 18), 幫助肯定測試只運行在 Android4.3(API level 18)或更高級別的設備上。(UI Automator 框架要求的)
UiObject cancelButton = device.findObject(new UiSelector() .text("Cancel") .className("android.widget.Button")); UiObject okButton = device.findObject(new UiSelector() .text("OK") .className("android.widget.Button")); // Simulate a user-click on the OK button, if found. if(okButton.exists() && okButton.isEnabled()) { okButton.click(); }
UiSelector 類:在當前顯示的用戶界面中查詢一個特定的元素。
UiObject appItem = device.findObject(new UiSelector() .className("android.widget.ListView") .instance(0) .childSelector(new UiSelector() .text("Apps")));
tips:
當獲取 UiObject 對象後,能夠調用 UiObject 類中的方法在其上執行相應操做:
經過 getContext() 方法獲取到 Context 後,能夠進行發送 Intent 或者啓動 Activity 的操做。
public void setUp() { ... // Launch a simple calculator app Context context = getInstrumentation().getContext(); Intent intent = context.getPackageManager() .getLaunchIntentForPackage(CALC_PACKAGE); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances context.startActivity(intent); device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT); }
UiCollection videos = new UiCollection(new UiSelector() .className("android.widget.FrameLayout")); // Retrieve the number of videos in this collection: int count = videos.getChildCount(new UiSelector() .className("android.widget.LinearLayout")); // Find a specific video and simulate a user-click on it UiObject video = videos.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "Cute Baby Laughing"); video.click(); // Simulate selecting a checkbox that is associated with the video UiObject checkBox = video.getChild(new UiSelector() .className("android.widget.Checkbox")); if(!checkBox.isSelected()) checkbox.click();
UiScrollable settingsItem = new UiScrollable(new UiSelector() .className("android.widget.ListView")); UiObject about = settingsItem.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "About tablet"); about.click();
InstrumentationTestCase 繼承自 TestCase,可使用標準的 JUnit Assert 方法進行結果驗證。
如下代碼片斷展現瞭如何驗證計算器加法:
private static final String CALC_PACKAGE = "com.myexample.calc"; public void testTwoPlusThreeEqualsFive() { // Enter an equation: 2 + 3 = ? device.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("two")).click(); device.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("plus")).click(); device.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("three")).click(); device.findObject(new UiSelector() .packageName(CALC_PACKAGE).resourceId("equals")).click(); // Verify the result = 5 UiObject result = device.findObject(By.res(CALC_PACKAGE, "result")); assertEquals("5", result.getText()); }
能夠經過 Android Studio 或者命令行運行 UI Automator tests. 確保項目的默認 instrumentation runner 是 AndroidJUnitRunner.
Samples:
https://github.com/googlesamples/android-testing/tree/master/ui/uiautomator/BasicSample 基礎的UI Automator 示例代碼
Codelabs:
https://codelabs.developers.google.com/codelabs/android-testing/index.html
歡迎關注微信公衆號"測試開發Stack"