自動化測試 | UI Automator 進階指南

UI Automator 相關介紹:html

  • 跨應用的用戶界面自動化測試
  • 包含在 AndroidX Test(https://developer.android.com/training/testing) 中
  • 支持的 Android 系統:>= Android 4.3 (API level 18)
  • 基於 instrumentation,依賴於 AndroidJUnitRunner 測試運行器

設置 UI Automator(Set up UI Automator)

在編寫測試代碼前,先確保如下兩個配置:
一、測試代碼存放位置
二、項目依賴(https://developer.android.com/training/testing/set-up-project)android

(1) 添加 Gradle 依賴(Add Gradle dependencies)git

  • 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-declarationsgithub

當前面的配置完成後,進行其餘配置:
app下的build.gralde:編程

dependencies {
    ...
    androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}

當全部配置都完成後,進行被測應用的 UI 組件分析,確保能被識別以及接入控制。微信

檢查設備上的用戶界面(Inspect the UI on a device)

uiautomatorviewer:app

(1) 啓動手機上的被測應用框架

(2) 手機鏈接電腦ide

(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 測試類中執行如下編程模型:

  1. 獲取一個 UiDevice 對象去接入測試設備,調用 getInstance() 方法,傳入 Instrumentation 對象做爲參數。
  2. 經過 UiObject 對象調用 findObject() 方法接入顯示在設備上的 UI 組件(例如,當前手機屏幕顯示的用戶界面)。
  3. 經過調用 UiObject 方法在 UI 組件上模擬一個交互的動做。例如,調用 performMultiPointerGesture() 方法模擬多指觸控,調用 setText() 方法編輯文本框。當測試包含多個 UI 組件或者更加複雜的操做序列時,在第二步和第三步中可重複調用各類 API.
  4. 當執行完這些用戶交互的動做後,檢查返回的結果是否符合預期。
    這些步驟在如下章節會講的更加詳細。

訪問用戶界面組件 (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"

相關文章
相關標籤/搜索