UiAutomator2.0(轉)


1.     概述

UI測試(功能測試、黑盒測試)不需要測試者了解應用程序的內部實現細節,只需要知道當執行了某些特定的動作后是否會得到其預期的輸出。這種測試方法,在團隊合作中可以更好地分離的開發和測試角色。

常見的UI測試的方法是手動去執行,然后去驗證程序是否達到的預期的效果,很顯然這種方法耗時、繁瑣並且很容易出錯。因此我們需要一種可靠的方法來進行UI測試,通過測試框架,我們可以完成針對具體使用場景的測試用例,然后可以循環的、自動的來運行我們的測試case。

 

Android的SDk提供了以下的工具來支持我們進行UI自動化測試:

uiautomatorviewer:一個用來掃描和分析android應用程序的UI控件的GUI工具。

uiautomator:一個包含創建測試、執行自動化測試API的Java庫。(Uiautomator文檔:http://android.toolib.NET/tools/help/uiautomator/index.html )

 

要使用這些工具,你必須安裝Android開發工具以下版本:

Android SDKTools:API 21 版本或者21以上版本;

Android SDKPlatform:API 16 版本或者16以上版本.

 

 

2.     UiAutomatorViewer使用

在你開始寫測試用例之前,使用uiautomatorviewer可以幫助你熟悉你的UI組件(包括視圖和控件)。你可以使用它對當前連接到你電腦上的手機屏幕進行一個快照,然后可以看到手機當前頁面的層級關系和每個控件的屬性。利用這些信息,你可以寫出針對特定UI控件的測試用例。

在 ..\sdk\tools\ 目錄下打開uiautomatorviewer.bat (打開前請手機連接電腦)

1)        獲取快照

當你要分析一個頁面時,首先將手機的頁面停留在你要分析的頁面,然后用數據線連接電腦。然后點擊uiautomatorviewer左上角的第二個圖標按鈕 Device Screenshot,點擊之后會將當前手機界面的快照更新到這里來。

2)        頁面層級

右上方的整個區域,就是當前頁面布局的層級關系。

3)        控件屬性

右下方的整個區域,是當前選中的頁面或者是控件的屬性信息。這部分比較重要,我們以后寫代碼的時候就是需要通過查看屬性中的控件的id或者是text等來獲取控件的實例,然后點擊操作它。

 

我們可以通過text、resource-id、class、content-desc等來獲取控件。

 

 

3.     UiAutomator

UiAutomator2.0做了一些改進:

1)      基於 Instrumentation,可以獲取應用 Context,使用 Android 服務及接口

2)      基於 Junit 4,測試用例無需繼承於任何父類,方法名不限,使用注解 Annotation 進行

UI 執行效率比 1.0 快,測試執行可使用Android Junit 方式及 gradle 方式

3)      API 更新,新增UiObject2、Until、By、BySelector 等:API For UI Automator

4)      Log 輸出變更,以往使用System.out.print 輸出流回顯至執行端,2.0 輸出至 Logcat。

 

 

4.     UiAutomator2.0使用步驟

1)        在android studio新建一個工程

2)        添加依賴

 

[java]  view plain  copy
 
  1. androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {  
  2.     exclude group:'com.android.support',module:'support-annotations'  
  3. })  
  4. testCompile 'junit:junit:4.12'  
  5. // Set this dependencyto build and run UI Automator tests  
  6. androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'  



 

3)        在androidTest目錄下新建一個Test, 點擊button4,跳轉到一個新頁面

@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class Test1 {
    private UiDevice mDevice;

    @Before
    public void before() {
        // Initialize UiDevice instance
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        assertThat(mDevice, notNullValue());

        // Start from the home screen
        mDevice.pressHome();
        // open app
        openApp("com.ut.anquanguankong");
    }

    @Test
    public void test() throws InterruptedException {
        //點擊desc=button4的按鈕
        findObject(By.desc("button4")).click();
    }

    public void openApp(String packageName) {
        Context context = InstrumentationRegistry.getInstrumentation().getContext();
        Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);
    }

    public UiObject2 findObject(BySelector selector) throws InterruptedException {
        UiObject2 object = null;
        int timeout = 30000;
        int delay = 1000;
        long time = System.currentTimeMillis();
        while (object == null) {
            object = mDevice.findObject(selector);
            sleep(delay);
            if (System.currentTimeMillis() - timeout > time) {
                break;
            }
        }
        return object;
    }
}

5.     UiAutomator2.0 API

UiAutomator2.0是兼用1.0的,2.0的API會包含1.0的API。通過了解這API方法,就可以編寫UI自動化測試代碼了。官方文檔:

https://developer.android.google.cn/reference/android/support/test/uiautomator/package-summary.html,下面介紹常用的2.0 API:

 

 

 

1)   InstrumentationRegistry

a)        類說明

一個暴露的注冊實例,持有instrumentation運行的進程和參數,還提供了一種簡便的方法調用instrumentation, application context和instrumentation參數。

b)        相關API

返回類型

API

static Bundle

getArguments(): 返回一個instrumentation參數副本

static Context

getContext():  返回instrumentation對應包的Context

InstrumentationRegistry.getContext() == instrumentation.getContext()

static Instrumentation

getInstrumentation(): 返回當前運行的instrumentation

static Context

getTargetContext(): 返回一個目標應用程序的Context

static void

registerInstance(Instrumentation instrumentation, Bundle arguments):記錄或暴露當前instrumentation運行和instrumentation參數包的副本,存儲在注冊中心

c)        示例

@Test
public void InstrumentationRegistryTest() {
    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    Context context1 = InstrumentationRegistry.getContext();
    Context context2 = InstrumentationRegistry.getTargetContext();
    Context context3= instrumentation.getContext();

    if(context1 == context2) {
        Log.i("Chris",  "InstrumentationRegistry getContext == getTargetContext");
    }else {
        Log.i("Chris",  "InstrumentationRegistry getContext != getTargetContext");
    }

    if(context1 == context3) {
        Log.i("Chris",  "InstrumentationRegistry getContext == Instrumentation getContext");
    }else {
        Log.i("Chris",  "InstrumentationRegistry getContext != Instrumentation getContext");
    }

    Intent intent = context2.getPackageManager().getLaunchIntentForPackage("xxx.xxx.xxx");
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    context2.startActivity(intent);
}

 

2)   UiDevice

a)        類說明

UiDevice用與訪問關設備狀態的信息,也可以使用這個類來模擬用戶在設備上的操作。可以通過下面的方法得到實例:

UiDevice mdevice = getUiDevice();

b)        相關API

返回類型

API

Boolean

click(int x, int y): 模擬用戶在指定位置點擊

String

getCurrentActivityName():  獲得的是應用程序在桌面上顯示的名字

String

getCurrentPackageName():獲得當前顯示的應用程序的包名

int

getDisplayHeight():獲得當前設備的屏幕分辨率的高

int

getDisplayWighth():獲得當前設備的屏幕分辨率的寬

boolean

isScreenOn():判斷手機當前是否滅屏

Void

wakeUp():點亮當前屏幕

Boolean

pressBack():點擊back鍵

Boolean

pressHome():點擊home鍵

Boolean

PressMenu():點擊menu鍵

Boolean

PressCode(int code): 利用keycode值模擬一次按下事件, 例如,需要按下數字1 數字1的keycode是 KEYCODE_NUMPAD_1,更多keycode可以在 http://developer.android.com/intl/zh-cn/reference/android/view/KeyEvent.html 進行查詢

boolean

swipe(int startX, int startY, int endX, int endY, int steps): 用指定的步長,從A點滑動B點

boolean

takeScreenshot(File storePath): 截取當前屏幕,保存到文件

 

UiAutomator2在UiDevice新增的API

返回類型

API

void

dumpWindowHierarchy(OutPutStream out): 獲取當前頁面層級到輸出流

String

executeShellCommand(String cmd): 執行一個shell命令。備注:此方法只支持api21以上,手機需要5.0系統以上

UiObject2

findObject(BySelector selector): 返回第一個匹配條件的對象

UiObject

findObject(UiSelector selector): 返回一個匹配條件的代表視圖的UiObject對象

List<UiObject2>

findObjects(BySelector selector): 返回所有匹配條件的對象

<R> R

wait(SearchCondition<R> condition, long timeout): 等待的條件得到滿足


3)   BySelector和By

a)        類說明

BySelector和By是UiAutomator2.0的類。

BySelector類為指定搜索條件進行匹配UI元素,通UiDevice.findObject(BySelector)方式進行使用。

By類是一個實用程序類,可以以簡潔的方式創建BySelectors對象。主要功能是使用縮短語法,提供靜態工廠方法來構造BySelectors對象。例如:你將使用findObject(By.text("foo")),而不是findObject(newSelector().text("foo"))的方式來查找文本值為“foo”的UI元素。

b)        相關API

在這里介紹By的API,BySelector的API和By的對應的。

返回類型

API

BySelector

clazz(String calssName),

clazz(String packageName, String className),

clazz(Class clazz),

clazz(Pattern className)

通過class來匹配UI

BySelector

desc(String contentDescription)

descContains(String substring)

descStartsWith(String substring)

descEndsWith(String substring)

desc(Pattern contentDescription)

通過contentDescription來匹配UI

BySelector

text(String contentDescription)

textContains(String substring)

textStartsWith(String substring)

textEndsWith(String substring)

text(Pattern contentDescription)

通過text來匹配UI

BySelector

res(String resourceName)

res(String resourcePackage, String resourceId)

res(Pattern resourceName)

通過id來匹配UI

BySelector

checkable(boolean isCheckable)

BySelector

clickable(boolean isClickable)

BySelector

enabled(boolean isEnabled)

BySelector

focusable(boolean isFocusable)

BySelector

focused(boolean isFocused)

BySelector

longClickable(boolean isLongClickable)

BySelector

scrollable(boolean isScrollable)

 

4)   UiObject2

1)        類說明

可以理解為直接操作界面ui元素的實例。UiObject2是UiAutomator2的類。

2)        相關API

基本動作

API

說明

clear()

清楚編輯框內的內容

click()

點擊一個對象

clickAndWait(EventCondition<R> condition, long timeout)

點擊一個對象然后等待在超時的時間內條件滿足則通過,否則拋出異常

drag(Point dest, int speed)

自定義速度拖拽這個對象到指定位置

drag(Point dest)

拖拽這個對象到指定位置

longClick()

長按某個對象

scroll(Direction direction, float percent)

對該對象執行一個滾動操作

scroll(Direction direction, float percent, int speed)

自定義速度,對該對象執行一個滾動操作

setText(String text)

設置文本內容

legacySetText(String text)

通過發送keycode,設置文本內容

 

手勢動作

API

說明

pinchClose(float percent, int speed)

自定義速度執行收縮手勢

pinchClose(float percent)

執行收縮手勢

pinchOpen(float percent, int speed)

自定義速度執行展開手勢

pinchOpen(float percent)

執行展開手勢

fling(Direction direction)

執行一個掃動手勢,Direction代表為起點方向

fling(Direction direction, int speed)

自定義速度,執行一個掃動手勢

swipe(Direction direction, float percent, int speed)

執行一個滑動操作,可自定義滑動距離和速度

swipe(Direction direction, float percent)

執行一個滑動操作

setGestureMargin(int margin)

以像素為單位,設置手勢邊緣

setGestureMargins(int left, int top, int right, int bottom)

以像素為單位,設置手勢邊緣

 

獲取層級與條件判斷

API

說明

findObject(BySelector selector)

搜索在這個對象之下的所有元素,並返回第一個與搜索條件匹配的

findObjects(BySelector selector)

搜索在這個對象之下的所有元素,並返回所有與搜索條件匹配的

getChildCount()

返回這個對象直屬子元素的數量

getChildren()

返回這個對象下的直接子元素的集合

getParent()

返回該對象的父類

equals(Object object)

比較兩個對象是否相等

hashCode()

獲取對象的哈希碼

hasObject(BySelector selector)

返回該對象是否存在

recycle()

回收該對象

wait(UiObject2Condition<R> condition, long timeout)

等待條件被滿足

wait(SearchCondition<R> condition, long timeout)

等待條件被滿足

 

5)   Configration

a)        類說明

Configrator用於設置腳本動作的默認延時:

1.        可調節兩個模擬動作之間的默認間隔

2.        可調節輸入文本的輸入時間間隔

3.        可調節每次滾動的時間間隔

4.        可調節等待系統空閑的默認時間

b)        相關API

延時項

默認延時

說明

API

動作

3s

設置延時

setActionAcknowledgmentTimeout(long timeout)

 

 

獲取默認延時

getActionAcknowledgmentTimeout()

鍵盤輸入

0s

設置延時

setKeyInjectionDelay(long delay)

 

 

獲取默認延時

getKeyInjectionDelay()

滾動

200ms

設置延時

setScrollAcknowledgmentTimeout(long timeout)

 

 

獲取默認延時

getScrollAcknowledgmentTimeout()

空閑

10s

設置延時

setWaitForIdleTimeout(long timeout)

 

 

獲取默認延時

getWaitForIdleTimeout()

組件查找

10s

設置延時

setWaitForSelectorTimeout(long timeout)

 

 

獲取默認延時

getWaitForSelectorTimeout()

 

 

 

6.     斷言

1)   斷言函數介紹

確定被測試的方法是否按照預期的效果正常工作

比如說:

if (假設成立){

    通過測試

}else{

    報錯並終止當前用例測試

}

2)   斷言函數用例結構

一個完整的測試用例必需要有斷言函數

setUp//初始化

//測試用例,junit4版本才可以使用多條用例

test        初始化場景與數據

test        模擬操作步驟

test        斷言

test        恢復場景   

tearDown//回收初始化垃圾

3)   斷言函數Java錯誤類型

a)        Error:

一般是指與虛擬機相關的問題,如系統崩潰,虛擬機錯誤,內存空間不足,方法調用棧溢出等。對於這類錯誤導致的應用程序中斷,僅靠程序本身無法恢復和預防(斷言)

b)        Exeeption:

表示程序可以處理的異常,可以捕獲且可能恢復。遇到這類異常,應該盡可能處理異常,使程序恢復運行,而不應該隨意終止異常(最常見的是UI對象找不到的異常)

 

 

 

4)   斷言函數API

 

例如:

//斷言兩個對象是否相等

asserEquals(Stringmessage,Object expected,Object actual){

    if (expected==null && actual==null){

        return ;   

    }

    if (expected!=null && expected.equals(actual)){

        return

    }

    failNotEquals(message,expected,actual);

}

參數

說明

Message

可選消息,在斷言失敗后會拋出這個消息

Expected

期望的值

Actual

實際的值

 

相關API

方法

說明

assertEquals(boolean,boolean)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,boolean,boolean)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(byte,byte)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,byte,byte)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(char,char)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,char,char)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(int,int)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,int,int)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(long,long)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,long,long)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(Object,Object)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,Object,Object)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(short,short)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,short,short)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,String)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(String,String,String)

如果期望(expected)和實際(actual)相等則通過,否則失敗

assertEquals(double,double,double)

如果期望(expected)和實際(actual)相差不超過精度值(delta)則通過,否則失敗

assertEquals(String,double,

double,double)

如果期望(expected)和實際(actual)相差不超過精度值(delta)則通過,否則失敗

assertEquals(float,float,float)

如果期望(expected)和實際(actual)相差不超過精度值(delta)則通過,否則失敗

assertEquals(String,float,float,float)

如果期望(expected)和實際(actual)相差不超過精度值(delta)則通過,否則失敗

assertFalse(boolean)

如果條件(condition)為False則通過,否則失敗

assertFalse(String,boolean)

如果條件(condition)為False則通過,否則失敗

assertTrue(boolran)

如果條件(condition)為True則通過,否則失敗

assertTrue(String,boolran)

如果條件(condition)為True則通過,否則失敗

assertNotNull(Object)

如果條件(condition)為非空則通過,否則失敗

assertNotNull(String,Object)

如果條件(condition)為非空則通過,否則失敗

assertNull(Object)

如果條件(condition)為空則通過,否則失敗

assertNull(String,Object)

如果條件(condition)為空則通過,否則失敗

assertNotSame(Object,object)

如果期望(expected)和實際(actual)引用不同的內存對象對象則通過,否則失敗

assertNoteSame(String,Object,Object)

如果期望(expected)和實際(actual)引用不同的內存對象對象則通過,否則失敗

assertSame(Object,Object)

如果期望(expected)和實際(actual)引用相同的內存對象對象則通過,否則失敗

assertSame(String,Object,Object)

如果期望(expected)和實際(actual)引用相同的內存對象對象則通過,否則失敗

fail()

用例立即失敗

fail(String)

用例立即失敗,且拋出指定消息

failNotEquals(String,Object,Object)

用例立即失敗,且拋出指定消息與期望、實際值不相等的消息

failNotSame(String,String,String)

用例立即失敗,且拋出指定消息與期望、實際值不相等的消息

failSame(String)

用例立即失敗,且拋出指定消息

 

 

 

8.     報告分析

1)   錯誤類型

斷言錯誤:就是斷言這個用例的成功或者失敗(AssrtionFailedError)

腳本錯誤:UiObjectNotFoundException(找不到對象異常)、java異常等

2)   報告分析

@Test
 public void testMain() throws InterruptedException, UiObjectNotFoundException {

    BySelector tabSelector = By.desc("TabContainer");
    uiAction.click(By.desc("button4")).isExist("打開主頁面出錯", tabSelector);
    mDevice.pressBack();
 }

這個方法測試:點擊button4,進入主頁面。

 

a)        正常運行

testMain打開一個存在的頁面。

run started: 1 tests 
TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
InteractionController: runAndwaitForEvents timed out waiting for events
QueryController: Got null root node from accessibility - Retrying...
InteractionController: runAndwaitForEvents timed out waiting for events
TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
TestRunner: run finished: 1 tests, 0 failed, 0 ignored

從上面報告來看,testMain正常執行。

 

b)        斷言錯誤

testMain打開一個不存在的頁面。

run started: 1 tests
TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
InteractionController: runAndwaitForEvents timed out waiting for events
QueryController: Got null root node from accessibility - Retrying...
TestRunner: failed: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
TestRunner: ----- begin exception -----
TestRunner: junit.framework.AssertionFailedError: 打開主頁面出錯
at junit.framework.Assert.fail(Assert.java:50)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertNotNull(Assert.java:218)
at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.isExist(UiAutomatorActionImpl.java:102)
at com.chris.example.uiautomatordemo.AnquanguankongTest.testMain(AnquanguankongTest.java:52)
TestRunner: ----- end exception -----
TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
TestRunner: run finished: 1 tests, 1 failed, 0 ignored

從上面報告來看,testMain執行失敗,並給出詳細的錯誤信息。

c)        腳本錯誤

testMain點擊一個不存在的button

run started: 1 tests
TestRunner: started: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
InteractionController: runAndwaitForEvents timed out waiting for events
QueryController: Got null root node from accessibility - Retrying...
TestRunner: failed: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
TestRunner: ----- begin exception -----
TestRunner: junit.framework.AssertionFailedError: BySelector [DESC='\Qbutton42\E'] no found
at junit.framework.Assert.fail(Assert.java:50)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertNotNull(Assert.java:218)
at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.isExist(UiAutomatorActionImpl.java:112)
at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.findObjectWithCheck(UiAutomatorActionImpl.java:75)
at com.chris.example.uiautomatordemo.UiAutomatorActionImpl.click(UiAutomatorActionImpl.java:84)
at com.chris.example.uiautomatordemo.AnquanguankongTest.testMain(AnquanguankongTest.java:52)
TestRunner: ----- end exception -----
TestRunner: finished: testMain(com.chris.example.uiautomatordemo.AnquanguankongTest)
MonitoringInstrumentation: Activities that are still in CREATED to STOPPED: 0
TestRunner: run finished: 1 tests, 1 failed, 0 ignored


免責聲明!

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



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