Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android app自動化測試之UIAutomator,androiduiautomator

android app自動化測試之UIAutomator,androiduiautomator

編輯:關於android開發

android app自動化測試之UIAutomator,androiduiautomator


一、UIAutomator

         Android自動化測試工具有很多,但是要免費、易上手,本人覺得就直接使用Eclipse自帶的UIAutomator就不錯。測試人員無需跟開發要代碼信息,只要手機上有安裝之後的APP自己就能做出自動測試用例,況且一通百通,就算是不滿足於UI測試的,找個簡單易上手的先明白原理,再深入了解其它復雜工具也會輕松很多。何樂而不為呢?

UIAutomator是Eclipse自帶的用於UI自動化測試工具,可仿真APP上的單擊、滑動、輸入文本等操作。

         在使用之前,需要安裝好java有關的JDK,SDK,然後配置java環境變量。關於安裝JDK,SDK,配置JAVA環境變量網上有數不清的教程,這裡就不廢話了。直接進入主題。

         還是簡單介紹一下自動化測試代碼中使用到的類關系:現在還不清楚也沒有關系,先讓程序跑起來,然後在使用API的過程中自然就理解了。

 

1、創建java工程

         打開Eclipse,操作步驟: 點擊“File—>New—>java Project”,然後打開如下圖的界面:

 

其它的先不用管,既然要求必須輸入Project name,那就輸入。請用英文命名,很多時候中文會帶來許多意想不到的問題。

輸入Project name之後,點擊“Next”進入下一步,看到的界面如下:

 

Source下的東西無需關注,點擊“Libraries”,既然是調用java自帶的UIAutomator,就要先引入UIAutomator使用到的包。導入uiautomator相關包:點擊“Add External JARs”

         先要找到自己的android.jar 和 Uiautomator.jar 包的位置,一般在安裝文件下的adt-bundle-windows-x86-20130917\sdk\platforms\android-18\ 下,具體要找到自己電腦上的jar位置哦,

 

選中兩個jar文件,“打開”導入。

此外還需要再加入本地庫,點擊“Add Library”,選擇Junit,後點擊“next”,使用默認的JUit3。點擊“Finish”。

 

回到New Java Project,點擊“Finish”即可創建工程成功。

 

2、創建class文件

         剛剛創建的工程,Src下還沒有任何東西,我們需要創建一個Class文件,用來寫自動化程序代碼。

創建class文件:項目名稱右鍵——NEW——class,寫class內容如下

 

         2.1 import UIautomator兩個包

         2.2 class 名稱必須和XXX.java名稱相同

         2.3每一個public開頭,且以test命名開始的函數,系統就視為一個case,運行時方法的調用順序並不是代碼中的位置順序,而是根據方法名的ASCII碼大小順序調用的。

         2.4每一個class都必須繼承自UiAutoMatorTestCase

3、生成xml 文件

 

 

找到target版本:>android list targets

Id:1

創建build.xml到測試工程中去,用於生成.jar文件:>Android create UItest-project –n testprojectname –t 1 –p testprojectpath

 

4.build jar 文件

         4.1 Update Ant to 1.9

         4.2 配置ant使用的javac版本:

                   右鍵build.xml—》RUN AS —》 External Tools Configurations。

        在main Tab 下輸入Argumens: -Dbuild.compiler=javac1.7——》

                   Apply。

 

                   4.3配置sdk中的build.xml 文件E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\ant\build.xml: 找到javac,添加includeantruntime="false"

<javac encoding="${java.encoding}"

                    source="${java.source}" target="${java.target}"

                    debug="true" extdirs="" includeantruntime="false"

                    destdir="${out.classes.absolute.dir}"

                    bootclasspathref="project.target.class.path"

                    verbose="${verbose}"

                    classpathref="project.javac.classpath"

                    fork="${need.javac.fork}" includeantruntime="false">

                <src path="${source.absolute.dir}" />

                <src path="${gen.absolute.dir}" />

                <compilerarg line="${java.compilerargs}" />

            </javac>

5. push jar文件到手機

Cmd環境下需要先找到adb.exe所在位置目錄,完整的命令語句:

adb push <path_to_output_jar>  /data/local/tmp

“<path_to_output_jar>”即要放到手機裡的jar文件名及路徑

 

相反的如果是從手機復制文件到電腦:

6. 運行測試

Cmd環境下需要先找到adb.exe所在位置目錄,完整的命令語句:

adb shell uiautomator runtest  XXX.jar  -c  XXXClassname

備注:XXX.jar需要運行的jar文件名,

       Classname: 需要運行jar中的哪個類的名字

 

 

7.測試結果

運行結果如下:

 

Current: 當前運行的測試編號,與方法名稱相關

Class:當前運行的函數所在的類

Numtests:測試的總數,每一個public testXX就是一個測試數

Test: 當前測試的函數名稱

INSTRUMENTAION_STATUS_CODE:測試狀態碼,1表示正在執行,0表示執行成功,-1編輯運行錯誤

二、使用UIAUTOMATORVIEWER獲取APP控件

1.手機打開app

使用UIAutomator獲取手機app控件時,先在手機上運行起對應app

2 .電腦連接手機

         確保電腦與手機已連通。

         先在cmd界面到達java的adb.exe所在位置,

         運行命令“ >adb shell ”獲取當前連接電腦的設備,如下圖,如果adb shell 運行正常,會彈出“shell@hwH30-C00:/$”

       有多個設備連接開發機器時(模擬器或真機),通過設置ANDROID_SERIAL環境變量指定需要截圖的設備。比如下圖的:shell@hwH30-C00:/$ set ANDROID_SERIAL= hwH30-C00,設置連接成功後,返回:“set ANDROID_SERIAL=hwH30-C00”

       adb devices命令查看連接中的設備編號

 

如果當前只連接了一個設備,則不用再在Cmd中執行連接命令

3.運行uiautomatorviewer.bat

找到uiautomatorviewer.bat安裝目錄,本機中的位置如下:

E:\Android\android\adt-bundle-windows-x86-20130917\sdk\tools\uiautomatorviewer.bat

雙擊運行uiautomatorviewer.bat,打開界面如下圖:

 

         上圖中左半部分顯示當前手機的呈現界面,若要獲取其它界面的控件,需要在手機上後,點擊上圖左上角頂部的刷新,重新獲取新界面。

         上圖右半部分顯示當前界面的所有ui元素層即控件信息。右上半部分顯示層級,右下部分顯示指定層級上具體的控件屬性,比如當前屬性“class”:當前控件的所在class

“Package”:顯示當前控制所在包

“Resource-id”:這個屬性有最好了,有些Android開發人員沒有為每個控件單獨命名,在抓取的時候就非常不方便。如果有Resource-id,在抓取控件時,直接用Resource-id就能獲取到正確的控件,並且操作正確,如果沒有Resource-id,就只能通過查找同類控件後再按順序獲取了。比如先找到所有的textbox控件,再從所有的textbox控件中按序取幾個,才能抓取到對應控件。

還有一些控件屬性顯示是否可見,是否checked,有實際應用需要時,可以獲取

這裡附上一個簡單的測試java源代碼,供參考代碼格式

import com.android.uiautomator.core.UiDevice;

import com.android.uiautomator.core.UiObject;

import com.android.uiautomator.core.UiObjectNotFoundException;

import com.android.uiautomator.core.UiScrollable;

import com.android.uiautomator.core.UiSelector;

import com.android.uiautomator.testrunner.UiAutomatorTestCase;

 

public class MyTestCase extends UiAutomatorTestCase {

    public void testDemo() throws UiObjectNotFoundException {

           //點擊home回到桌面

       UiDevice device = getUiDevice();

       device.pressHome();

           // 點擊並等待打開app

       UiObject czmApp = new UiObject(new UiSelector().className(android.widget.TextView.class.getName()));     

        czmApp.clickAndWaitForNewWindow(); 

        sleep(1000);

        //登錄;instan從0開始,

        UiObject username= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/login_username_edit"));  

        username.clearTextField();

        username.setText("15300000018");

        UiObject pwd= new UiObject(new UiSelector().className("android.widget.EditText").enabled(true).resourceId("com.wlyc.warehousechampions:id/login_password_edit"));

        pwd.clearTextField();

        pwd.setText("00OA8C");

        device.pressBack();

       sleep(500);

        UiObject enter= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/login_btn"));

        enter.click();

        sleep(5000);

                

        //跳轉到“我的”

        UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));  

        My.clickAndWaitForNewWindow();

        sleep(500);

        //檢查認證狀態

        UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));  

        int n=result.getText().length();

        //認證審核中

        n=Dur_verify(n); 

        //認證失敗 或者認證成功

        if(n==4)

        {

        UiObject failed= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/apply_certificate_layout"));  

        failed.click();

        UiObject isedit= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_right_layout"));  

           //認證失敗

        if(isedit.exists())

        {

           isedit.click();

           write();

           sleep(500);  

           }

        else //認證成功

        {  

            UiObject backtoMy= new UiObject(new UiSelector().className("android.widget.LinearLayout").resourceId("com.wlyc.warehousechampions:id/titlebar_left_layout"));

            backtoMy.click();

            Release();

           }

        }

        n=result.getText().length();

        n=Dur_verify(n);

        Release();

        }

              //錄入並提交企業認證

    public void write()throws UiObjectNotFoundException

        {

            //跳轉到企業認證

        UiObject Company= new UiObject(new UiSelector().className("android.widget.RadioButton").resourceId("com.wlyc.warehousechampions:id/company_certificate"));  

           Company.click();

           //錄入企業認證信息

           UiObject CompanyName=new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/cettificate_companyname_edit"));

           CompanyName.setText("test");

           sleep(500);

           //拍照

           UiObject photo= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.wlyc.warehousechampions:id/uploadimage_image"));  

           photo.click();

           sleep(500);

           UiObject takephoto= new UiObject(new UiSelector().className("android.widget.TextView").instance(1));  

           takephoto.click();

           sleep(500);

           UiObject phototake= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/shutter_button"));  

           phototake.click();

           sleep(500);

           UiObject photook= new UiObject(new UiSelector().className("android.widget.ImageView").resourceId("com.android.gallery3d:id/btn_done"));  

           photook.click();

           sleep(500);

           //提交認證

           UiObject commit= new UiObject(new UiSelector().className("android.widget.Button").resourceId("com.wlyc.warehousechampions:id/commit_btn"));  

           commit.click();

           sleep(500);   

        }

              //發布倉庫

        public void Release() throws UiObjectNotFoundException

        {

        UiObject wh_release= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/warehouse_distribute"));  

        wh_release.click();

         UiObject wh_name= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_name_edit"));  

        wh_name.setText("nametest");

        UiObject wh_ctg= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout"));  

        wh_ctg.clickAndWaitForNewWindow();

        UiObject wh_ctg1= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/spinnerview_listitem_text"));

            wh_ctg1.click();

            UiObject wh_mod= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/spinnerview_layout").instance(1));  

        wh_mod.click();

        sleep(500);

        UiObject wh_mod1= new UiObject(new UiSelector().className("android.widget.TextView").enabled(true).instance(0));  

        wh_mod1.click();

       

        sleep(500);

            UiObject wh_area= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_area_edit"));  

        wh_area.setText("5000");

        UiObject wh_area_low= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_rentarea_edit"));  

        wh_area_low.setText("500");

        UiObject wh_area_cpb= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_availrentarea_edit"));  

        wh_area_cpb.setText("1000");

        UiObject wh_pric= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_price_edit"));  

        wh_pric.setText("26");

        UiObject wh_etpris= new UiObject(new UiSelector().className("android.widget.EditText").resourceId("com.wlyc.warehousechampions:id/warehouse_company_edit"));  

        wh_etpris.setText("companyname");

        UiObject wh_floor= new UiObject(new UiSelector().className("android.widget.CheckBox").enabled(true).instance(0));  

        wh_floor.click();

        sleep(500);

                //向下滑動屏幕

        UiScrollable  wh_scroll=new UiScrollable(new UiSelector().className("com.wlyc.warehousechampions:id/pulltorefresh_listview"));

            wh_scroll.setAsVerticalList();

           ;

        UiObject wh_addre= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/warehousedetail_hightemperature_unit"));

            wh_addre.click();

            

        }

            //認證審核中等待

        public int Dur_verify(int n) throws UiObjectNotFoundException

        {

         while (n!=4) {

            UiObject My= new UiObject(new UiSelector().className("android.widget.RelativeLayout").resourceId("com.wlyc.warehousechampions:id/personcenter"));

            My.clickAndWaitForNewWindow();

               sleep(5000);

               UiObject result= new UiObject(new UiSelector().className("android.widget.TextView").resourceId("com.wlyc.warehousechampions:id/personcenter_apply_status"));

               n=result.getText().length();

           }

         return n;

        }

   

}

三、Uiautomator Api分析

         UiAutomator主要涉及以下幾個類,大多數位於源碼包的com.android.uiautomator.core下,其中粗體字部分為主要會接觸到的類,熟知這5個類的作用,就可以大體順暢的寫出UiAutomator的測試用例。

UiAutomatorTestCase

UiDevice

UiSelector

UiScrollable

UiObject

UiCollection

UiTestAutomationBridge, UiAutomatorBridge

InteractionController, QueryController

UiWatcher

                   1、UiAutomatorTestCase

TestCase (Junit) -> UiAutomatorTestCase  -> App Test

         每個測試用例(類)都需要繼承UiAutomatorTestCase,以實現測試環境的setup,teardown(拆卸)等同能。而UiAutomatorTestCase則是通過繼承Junit3中的TestCase類,並在其中的setUp() 、tearDown() 、getParams() 函數中。其中主要是用Bundle實現Android Activity之間的通訊。在UiAutomatorTestCase,還加入了getUiDevice()等關於UiDevice的 函數,以實現在測試的任意地方均可調用UiDevice()。

     2、UiDevice

         此類主要包含了獲取設備狀態信息,和模擬用戶至於設備的操作兩類api。

可以通過getDisplaySizeDp(), getDisplayWidth() , getDisplayHeight() ,getProductName() ,getCurrentActivityName(), getCurrentPackageName() 等獲取設備相關信息。

pressMenu(), pressBack(), pressHome(), pressSearch() ,pressDPadCenter(), pressDPadRight(), pressDPadLeft(), pressDPadUp(), pressDPadDown() ,pressDelete(), pressEnter(), pressKeyCode(), pressRecentApps(),click(),swipe(),getDisplayRotation() setOrientationLeft()… wakeUp(), sleep() ,dumpWindowHierarchy(), waitForWindowUpdate()等API可以靈活的操縱設備。

而takeScreenshot() 允許隨時對設備截屏。

     3、UiSelector

         主要是通過一定查詢方式,定位到所要操作的UI元素。

         一般UI元素均可通過以下API定位:text(), textMatches(String regex), textStartsWith(), textContains() ,className() ,classNameMatches(String regex), className(Class type) ,Description(), descriptionMatches(String regex),descriptionStartsWith(),descriptionContains() ,packageName(), packageNameMatches(String regex)。

值得注意的是index()和 instance() 兩個函數,其中index()是當前頁面的ID編號,instance()則表示在一定的搜索結果下,獲取的子元素集的第幾個元素。如:

new UiSelector().className("android.widget.ImageView").enabled(true).instance(2);

         另有enabled(), focused(), focusable(), scrollable(), selected(), checked(), clickable() ,longClickable() ,childSelector()等檢索條件,顧名思義。

     4、UiObject

         UiObject可代表頁面的任意元素,它的各種屬性定位通常通過UiSelector來完成。

         比較常用的Api如clickAndWaitForNewWindow(),表示點擊該元素,並且等待新窗口的展示完畢。這一過程是Android UI Testing框架支持的,不需要額外的控制等待時間。

         UiObject允許點擊該元素的具體一個部分,Api如clickTopLeft(), longClickBottomRight(),…

         通過getText(), getContentDescription(), getVisibleBounds(),… 等api可獲取UiObject的相關屬性,getPackageName() 可用來明確是否打開了目標測試的App.

         setText(), clearTextField() 可以 用來設置以及清空所關聯的輸入框。

         waitForExists() 可以用來操縱相關等待或驗證。

     5、UiCollection

         UiCollection一般與UiSelector連用,如它的構造函數也要求提供Uiselector: UiCollection(UiSelector selector)。

它的api較少,主要用以從Uiselector篩選出的元素集中挑出所要的元素:getChildByDescription(), getChildByInstance(), getChildByText() ,以及統計元素集的個數getChildCount()

     6、UiScrollable

UiObject -> UiCollection ->UiScrollable

         UiScrollable 用來表示可以滑動的界面元素,其繼承關系如上圖所示。

其Api中,setAsVerticalList(), setAsHorizontalList() 用以設置Ui元素列表是基於橫向滾動還是縱向滾動。其後可以用getMaxSearchSwipes() ,flingForward(), flingBackward() ,scrollForward(),scrollBackward() ,scrollToEnd(), scrollToBeginning() 等函數控制滑動,以及getChildByDescription(), getChildByInstance(), getChildByText() ,scrollIntoView(), scrollTextIntoView(),… 來選擇是否已經轉換到具有目標元素的頁面。如:

UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));

appViews.setAsHorizontalList();

UiObject helperApp;

helperApp = appViews.getChildByText(new UiSelector()

.className(android.widget.TextView.class.getName()), " 91助手 ");  則若當前頁面沒有91助手APP, 測試會自動滑動頁面,直到91助手App出現。

         下面介紹下UI Testing Framework構成的重要類:

7、UiTestAutomationBridge

         這是整個Testing Framework的基礎,此類負責連接系統了,記錄最新的可鏈接事件(AccessibilityEvent) , 窗口內容查詢Api等。可以被Android App調用,或者Java程序從shell調用。

這裡需要注意兩個概念:

         7.1、AccessibilityEvent:所有的Ui元素可以被操縱,因為這些Event都是AccessibilityEvent。對於怎樣令頁面元素可以被操縱,使得相關的事件都是AccessibilityEvent,請參見Uiautomator 詞條-"確認程序可以被測試" 部分。

         7.2、AccessibilityNodeInfo:視窗中的組件樹節點,也就是uiautomtorViewer中展示的各個節點。

         Api中connect(), disconnect() 負責建立與設備的實際連接。

executeCommandAndWaitForAccessibilityEvent() performAccessibilityAction() findAccessibilityNodeInfosByText(), findAccessibilityNodeInfoByViewIdInActiveWindow() 都是其中重要的Api。

     8、UiAutomatorBridge

         UiAutomatorBridge是UiTestAutomationBridge的子類,區別主要是在構造函數中加上了InteractionController 和QueryController 兩大對象的調用。以及一些常量定義等。除了上述差異,UiAutomatorBridge還定義了executeCommandAndWaitForAccessibilityEvent() 、onAccessibilityEvent() 、waitForIdle() 、addAccessibilityEventListener() 等函數。

     9、InteractionController

         介紹InteractionController,需要先提InteractionProvider,它負責注入用戶事件(如點擊、輸入等) ,並且反應事件的對應坐標。

         InteractionController則定義了幾乎所有至於手機的基礎操作,如runAndWaitForEvents(), clickAndWaitForEvents() ,click(), longTap(), scrollSwipe(),Swipe() ,clickAndWaitForNewWindow() ,touchUp(), touchDown(), TouchMove() ,isNaturalRotation(), setRotationRight(), setRotationLeft() ,freezeRotation() ,wakeDevice(), sleepDevice() 等。

     10、QueryController

         QueryController負責把UiSelector 的查找信息轉化為AccessibilityNodeInfo。

具體Api如下:findNodePatternRecursive(), translatePatternSelector(), translateReqularSelector(), translateCompoundSelector(), getRootNode() ,findAccessibilityNodeInfo()。

     11、UiWatcher

         UiWatcher只在UiSelector無法找到匹配的結果時被調用,意在重試、等待頁面更新 (如彈出對話框)等。其中只有一個主要函數:checkForCondition() 。

它的相關函數均在UiDevice中,如:UiDevice.registerWatcher() ,UiDevice. resetWatcherTriggers() ,UiDevice.runWatchers() ,UiDevice.removeWatcher()

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved