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


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

  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"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM