UI Automator 相關介紹:
- 跨應用的用戶界面自動化測試
- 包含在 AndroidX Test(https://developer.android.com/training/testing) 中
- 支持的 Android 系統:>= Android 4.3 (API level 18)
- 基於 instrumentation,依賴於 AndroidJUnitRunner 測試運行器
設置 UI Automator(Set up UI Automator)
在編寫測試代碼前,先確保以下兩個配置:
1、測試代碼存放位置
2、項目依賴(https://developer.android.com/training/testing/set-up-project)
(1) 添加 Gradle 依賴(Add Gradle dependencies)
- app 目錄下的 build.gradle 添加:
allprojects {
repositories {
jcenter()
google()
}
}
- dependencies 添加需要的 AndroidX Test Package, 比如:
dependencies {
...
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}
- 如果測試代碼需要基於 junit 的類,比如 Assert 和 TestSuiteLoader,在 android 區塊中添加(只需要添加需要用到的 library:https://developer.android.com/training/testing/set-up-project#junit-based-libs):
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-declarations
當前面的配置完成后,進行其他配置:
app下的build.gralde:
dependencies {
...
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}
當所有配置都完成后,進行被測應用的 UI 組件分析,確保能被識別以及接入控制。
檢查設備上的用戶界面(Inspect the UI on a device)
uiautomatorviewer:
(1) 啟動手機上的被測應用
(2) 手機連接電腦
(3) 打開 Terminal, 進入目錄 <android-sdk>/tools/
(4) 運行:uiautomatorviewer
查看應用的用戶界面屬性:
(1) 點擊左上角 "Device Screenshot" 按鈕
(2) 左邊是 UI 組件,右下半部分是屬性,右上半部分是布局層級
(3) 可選功能:點擊右上角 "Toggle NAF Nodes" 按鈕(黃色三角形,內有感嘆號),查看無法被識別/訪問的UI組件。---這個功能我都沒搞懂怎么用,點擊后貌似沒效果
確保 activity 可訪問(Ensure your activity is accessible)
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 測試類(Create a UI Automator test class)
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 對象去接入測試設備,調用 getInstance() 方法,傳入 Instrumentation 對象作為參數。
- 通過 UiObject 對象調用 findObject() 方法接入顯示在設備上的 UI 組件(例如,當前手機屏幕顯示的用戶界面)。
- 通過調用 UiObject 方法在 UI 組件上模擬一個交互的動作。例如,調用 performMultiPointerGesture() 方法模擬多指觸控,調用 setText() 方法編輯文本框。當測試包含多個 UI 組件或者更加復雜的操作序列時,在第二步和第三步中可重復調用各種 API.
- 當執行完這些用戶交互的動作后,檢查返回的結果是否符合預期。
這些步驟在以下章節會講的更加詳細。
訪問用戶界面組件 (Access UI components)
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 框架要求的)
- findObject()
- UiObject
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();
}
指定一個選擇器(Specify a selector)
UiSelector 類:在當前顯示的用戶界面中查詢一個特定的元素。
- childSelector()
- UiAutomatorObjectNotFoundException
UiObject appItem = device.findObject(new UiSelector()
.className("android.widget.ListView")
.instance(0)
.childSelector(new UiSelector()
.text("Apps")));
tips:
- 如果在一個頁面上找到一個以上的相同元素,自動返回第一個匹配的元素作為目標 UiObject.
- 可以通過整合多個屬性來縮小搜索范圍。
- 如果沒有找到目標元素,拋出 UiAutomatorObjectNotFoundException 異常。
- 可以使用 childSelector() 方法縮小多個 UiSelector 實例范圍。
- 如果有 Resource ID, 用這個代替 text 和 content-descripter.
- text 元素比較脆弱,有多種原因可能導致測試失敗。(比如:多語言)
在選擇區域中去明確一個對象狀態是非常有用的。比如:選擇一個已選中的列表以進行取消選中狀態,調用 checked() 方法,將參數設為 "true".
執行動作(Perform actions)
當獲取 UiObject 對象后,可以調用 UiObject 類中的方法在其上執行相應操作:
- click(): 點擊
- dragTo(): 拖動
- setText(): 設置文本
- clearTextField(): 清空文本
- swipeUp(): 向上滑動
- swipeDown(): 向下滑動
- swipeLeft(): 向左滑動
- swipeRight(): 向右滑動
通過 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);
}
在集合上執行動作(Perform actions on collections)
- UiCollection 類:在一個 item 的集合上模擬用戶操作(例如,歌曲列表或者郵件列表)。
如何創建一個 UiCollection 對象:明確一個搜索UI容器或者其他子 UI 元素集合的 UiSelector. 例如,包含子 UI 元素的 layout 視圖。
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();
在可滾動視圖中執行動作(Perform actions on scrollable views)
- UiScrollable 類:在手機屏幕上模擬垂直或者水平的滾動操作。這個類適用於當需要找到屏幕外的 UI 元素時,可以通過滾動操作將這個 UI 元素帶到屏幕內。
UiScrollable settingsItem = new UiScrollable(new UiSelector()
.className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
.className("android.widget.LinearLayout"), "About tablet");
about.click();
驗證結果(Verify results)
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());
}
在設備或虛擬機上運行 UI Automator 測試用例(Run UI Automator tests on a device or emulator)
可以通過 Android Studio 或者命令行運行 UI Automator tests. 確保項目的默認 instrumentation runner 是 AndroidJUnitRunner.
參考資料(Additional resources)
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"
