1. 程式人生 > >5小時搞定谷歌原生自動化框架UiAutomator1.0

5小時搞定谷歌原生自動化框架UiAutomator1.0

轉載地址:http://tmq.qq.com/2016/06/androidautotestframwork-uiautomator/

谷歌對UI測試(UI Tetsting)的概念是:確保使用者在一系列操作過程中(例如鍵盤輸入、點選選單、彈出對話方塊、影象顯示以及其他UI控制元件的改變),你的應用程式做出正確的UI響應。

  UI測試(功能測試、黑盒測試)的好處是不需要測試者瞭解應用程式的內部實現細節,只需要知道當執行了某些特定的動作後是否會得到其預期的輸出。這種測試方法,在團隊合作中可以更好地分離的開發和測試角色。然而常見的UI測試多是以手動方式去執行,然後去驗證程式是否達到的預期的效果,很顯然這種方法耗時、繁瑣並且很容易出錯。因此我們需要一種可靠的方法來進行UI測試,通過測試框架,我們可以完成針對具體使用場景的測試用例,然後可以迴圈的、自動的來執行我們的測試case。

  所以谷歌推出了下面的UI自動化測試框架。

初探

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

     uiautomatorviewer:一個用來掃描和分析Android應用程式的UI控制元件的GUI工具。
    uiautomator:一個包含建立測試、執行自動化測試API的java庫。(照例送上谷歌Uiautomator文件:http://android.toolib.net/tools/help/uiautomator/index.html )

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

    Android SDK Tools:API 21 版本或者21以上版本;
    Android SDK Platform:API 16 版本或者16以上版本.

  下面是自動UI測試所需的步驟的簡短概述:
    1、安裝待測應用到手機,通過uiautomatorviewer分析應用程式介面的控制元件,並確保應用程式的控制元件可以被自動化框架訪問。
    2、建立自動化測試用例來模擬你和應用程式之間互動的步驟。
    3、將測試用例編譯成一個JAR檔案,並發動到應用程式安裝的那臺測試裝置上。
    4、執行測試,檢視測試結果。
    5、修改測試過程中發現的bug。

分析控制元件

  在你開始寫測試用例之前,使用uiautomatorviewer可以幫助你熟悉你的UI元件(包括檢視和控制元件)。你可以使用它對當前連線到你電腦上的手機螢幕進行一個快照,然後可以看到手機當前頁面的層級關係和每個控制元件的屬性。利用這些資訊,你可以寫出針對特定UI控制元件的測試用例。

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

  想必大家看了上面的動態圖,基本上已經瞭解了一些用法了吧,我再進一步說明一下:   
  1、獲取快照:    
  當你要分析一個頁面時,首先將手機的頁面停留在你要分析的頁面,然後用資料線連線電腦。然後點選uiautomatorviewer左上角的第二個圖示按鈕 Device Screenshot,點選之後會將當前手機介面的快照更新到這裡來。  
  2、頁面層級:    
  右上方的整個區域,就是當前頁面佈局的層級關係。如果對Android五大布局比較熟悉的話,理解這一層應該不是問題。  
  3、不可用區域:    
  右上方的整個區域中的第二個按鈕Toggle NAF Nodes,按下後出現的黃色區域代表,這些控制元件是不被Uiautomator工具識別,無法獲取到這些控制元件的例項。以QQ首頁為例。  

  我們可以看到,當按下該按鈕的時候,下方的三個tab出現黃色區域,這就代表這三個區域的控制元件,如果你想通過Uiautomator提供的API來獲得他們的屬性,或者對其進行點選操作,是做不到的,因為你沒辦法拿到這些控制元件的例項。
  4、屬性詳情:
  右下方的整個區域,是當前選中的頁面或者是控制元件的屬性資訊。這部分比較重要,我們以後寫程式碼的時候就是需要通過檢視屬性中的控制元件的id或者是text等來獲取控制元件的例項,然後點選操作它。
  以QQ左上角的頭像控制元件為例:

  點選左上角的頭像控制元件之後,右下方區域就會顯示這個控制元件的詳細資訊。比如這裡我們可以得知它的resource-id就是com.tencent.mobileqq:id/conversation_head。

  然後利用Uiautomator的API方法就可以得到該控制元件的例項。

  上面的方法就是知道了該控制元件的id之後,模擬點選該控制元件的過程,當然Uiautomator還提供了根據text來獲取控制元件。

  這種點選的方法比起Monkeyrunner來說它的好處就是:Monkeyrunner是座標點選,當一個指令碼寫好後,換一個解析度的手機去執行,點選的位置可能就會出錯,而Uiautomator點選是先找到該控制元件,然後再點選該控制元件,因此可移植性比Monkeyrunner要好;另外程式碼的易讀性也更好一些。

環境搭建

  網上關於環境搭建的內容很多,也非常的詳細,這裡我就簡單的說下大體流程和注意事項吧。

  1、在Eclipse中建立一個Java的工程。       

  2、右鍵選中你建立的工程,在Properties > Java Build Path中:

    a、點選 Add Library > JUnit 新增JUnit3/4;

    b、點選Add External JARs… 匯入 uiautomator.jar and android.jar 這兩個jar包。

    這裡需要注意,匯入這兩個jar包的時候,注意Android的版本號,後面生成build.xml的時候需要知道你匯入的這兩個jar是哪個sdk版本的。

  3、匯入成功之後,就可以寫程式碼了。程式碼的格式參考下面:


  4、使用 android create uitest-project -n %工程名% -t 5 -p %工程目錄% 來生存build.xml檔案。需要注意的就是 這裡的 -t 後面的 5 就是Android list後對應的你當初引入兩個jar包的sdk版本對應的id。我的是 id: 5 or "android-19",所以我這裡是5 


  5、生成的build.xml 用ant工具進行編譯。編譯後會生成“工程名.jar”包(注意這裡需要使用 ant build命令來打包,這樣有錯誤可以看到);  
  6、將該jar包push到手機的 /data/local/tmp 目錄下  
  7、在adb shell 中執行: uiautomator runtest 工程名.jar -c 包名.類名  

簡單的例子

  以一個簡單的例子開始吧。我們完成一個 " 開啟QQ,進入QQ空間,然後退出 " 的case。

  程式碼如下:

  指令碼的執行效果如下:

  

  針對上面的例子的程式碼,我對每一句程式碼都做個詳細的解釋吧。  

  第一部分:啟動應用    

  exec() 這個函式的意思,相當於是在你在輸入adb shell 命令後,在Android手機系統的命令列下執行。所以上面這句話的意思和我們開啟cmd框輸入" adb shell am start *** " 是一樣的的效果。      

 一般來說我們做App的自動化的時候,第一步都是把App開啟,這個am start命令的就可以幫我們實現,類似與Monkeyrunner API中的startActivity() 函式。  

 第二部分:點選 “動態” tab  

  UiDevice物件會在API部分詳細講解,它是一個我們在Uiautomator中經常使用的一個物件。       

  這裡我們首先用它獲取到當前手機的寬和高的畫素。然後觀察到 “動態” tab位於右下方,因此在取得右下角的座標點後,又進行了一個大概的座標變化(這裡為了簡單只是向左和向上移動了50畫素,如果要精確的可以進行等比轉化),然後點選該座標。  

  這裡之所以用點選座標的方法,一方面是因為這個控制元件Uiautomator不支援用API獲得例項(上一節所說的NAF Nodes,如下圖),另一方面也是想說明在一些控制元件沒有固定的id、text和desc的時候,我們應該怎麼處理。   


   

  第三部分:點選 “好友動態” 

  要想操作一個控制元件(例如),首先得獲得一個UiObject物件,而UiObject物件可以通過UiSelector來構造,而UiSelector可以根據控制元件的id、text、content-desc來進行構造,這裡就是用content-desc來構造。


  如上圖用 uiautomatorviewer 查到該控制元件的 content-desc 的內容是 “點選進入好友動態” ,因此我們就可以通過程式碼中的方法來得到UiObject物件了,然後呼叫click() 方法來達到點選效果。       

  第四部分:點選左上角返回按鈕
  同第三部分的方法,找到id後直接獲得到UiObject物件,進行點選。
 
  第五部分:點選選單鍵 
  UiDevice 可以模擬點選home、back、menu 這三個鍵,程式碼應該大家都懂的怎麼變化了吧。 


  第六部分:退出    
  這一部分也是先通過獲取出控制元件屬性中的text值,然後構造出UiObject物件,完成點選。   
  以上部分內容就是整個操作QQ這個小例子的全部程式碼講解,看完之後對寫Uiautomator程式碼有了更進一步的瞭解了吧。其實你去仔細查閱API後會發現API可以支援我們做更多的事情。列舉出三個重要的API連結:

  UiDevice:http://wear.techbrood.com/tools/help/uiautomator/UiDevice.html  

  UiObject:http://wear.techbrood.com/tools/help/uiautomator/UiObject.html

  UiSelector:http://wear.techbrood.com/tools/help/uiautomator/UiSelector.html

如何更高效

  到此為止,我們已經瞭解Uiautomator的基本知識,並且也有了API的參考文件,因此對於我們來說完成一個UI自動化測試指令碼並不難,但是如何將UI自動化應用在實際的專案中,幫我們提高測試的效率呢?本節我們就說說,UI自動化應該怎麼去完成。       

  ​首先我們需要思考,在我們的編碼中是否有一些公共的方法可以提取出來做為一個單獨的函式呢?下面我列舉了一些供大家參考:

  1、點選操作

  首先,點選的操作是Uiautomator中用的最多的,而根據控制元件id和text來做為索引則是更多的。因此我們封裝如下的內容:


  使用上面我的方法封裝之後,你只需要呼叫 ClickByText("通訊錄"); 即可完成對"通訊錄" 這個控制元件的點選,並且在因為異常情況獲取不到該控制元件的時候,也不會報出異常。

  然而,我們去點選一個控制元件的時候,當它出現找不到的情況的時候,這有可能就是bug了,我們需要將其記錄下來,並且記錄下當時的現場,一般採用截圖的方法,以便我們查問題時候能更直觀的瞭解到當時機器一個執行情況。因此接下來,我要說說截圖和異常處理。

  2、截圖和異常處理

  上面的程式碼中,當UiObject物件找不到的時候,我們只是返回了一個false,告訴呼叫者這次呼叫失敗了,但是為什麼失敗,怎麼避免這樣的失敗,並沒有記錄下來。因此在這段程式碼中,我們需要加以下的內容:


 

  這樣當我們在呼叫 ClickByText("通訊錄"); 找不到控制元件的時候,我們的指令碼就會自動擷取當時螢幕的影象儲存在我們的手機中(如下圖),這樣我們只需開啟圖片,就知道當時發生了什麼,為什麼沒有找到該控制元件。     
  看似完美的方案,其實在實際執行中只是幫我們記錄了這個控制元件這一時刻點選失敗的原因,而我們想要的是,指令碼在呼叫了這個方法後,盡最大的可能幫我們點選成功。舉一個簡單的例子:  

  這是我們寫指令碼中經常遇到的一個問題,我們需要 ‘在A頁面上點選“進入”按鈕,跳轉到B頁面,然後點選B頁面上的“儲存”按鈕’ 完成我們的操作。

  一般我們的寫法是:

  ClickByText("進入");

  ClickByText("儲存");

  然而當我們的手機特別卡,或者是頁面承載太多東西的時候,當你呼叫了點選“進入”按鈕後,B頁面沒有及時的跳轉出來,這個時候呼叫B頁面上的“儲存”按鈕,就會出現異常,而如果你沒有按照我上面的方案去實現的話,系統就會丟擲異常,而使用了我上面的方案之後,系統雖然不會丟擲異常,而且會在你找不到B頁面的“儲存”按鈕時擷取當前的螢幕,你完全可以根據截圖來判斷出來:當是沒有找到“儲存”按鈕的原因是,當時的B頁面還沒有跳轉出來。然而在這個時候,我最希望的並不是看到日誌告訴我說哪里哪里失敗了,而是想讓這次的點選效果生效。

  那麼怎麼解決這個問題呢?相信很多親手寫過Uiautomator指令碼的朋友都知道,在兩個操作直接加如sleep,沒錯,這是解決方案,那麼究竟應該slepp多久呢?因為不同的手機響應時間是不一樣的,如果sleep太短就依然存在上述問題;如果sleep太長的話,無疑使得指令碼的執行變的緩慢,多出寫無用的sleep。因此我們需要去掉if判斷的程式碼,改為在while迴圈中等待這個控制元件的出現,一共等待5次,如果到了第五次,它還沒有出現的話,那麼我們就認為它真的不會出現了,這個時候去截圖比第一次就沒有找到更加的有意義。當然如果你還想提高你的UI自動化的健壯性,那麼這裡還可以加一個類似這樣的函式:


  這個 SolveProblems() 函式主要是用來解決一些“麻煩”的,例如我們在操作地圖的時候,當gps訊號不好的時候,就會彈出下面的對話方塊: 

  由於出現的對話方塊,遮擋住了我們的Activity,影響我們對介面上ui元素的獲取,這個時候,我們就可以在SolveProblems() 加入這樣一斷邏輯:當出現“開啟gps”對話方塊的時候,就點選“殘忍的拒絕”,將此對話方塊給關掉,這樣while的判斷條件再次執行的時候,就可以成功獲取到你想要的元素。

  所以說這個SolveProblems()才是提高UI自動化成功率的關鍵,因為每個App都有自己的特徵,因此這部分的內容,需要你們在平時的日積月累中才能總結出來,當你有了一個足夠多的經驗庫之後,你的App幾乎不會再因為外界因素而導致失敗了。經過我自己在我專案上的嘗試,效果非常的顯著。  

  ​3、日誌

  日誌的重要性不言而喻,當我們在自動化執行的過程中,肯定不會一直盯著螢幕觀察,因此日誌使我們最依靠的東西。關於日誌的記錄方法多種多樣,我這裡提供下我是怎麼在Uiautomator中列印日誌的: 

  接下來就是把這個函式加在一些關鍵的地方,當出錯的時候,方便我們排查問題即可。

總結

  將上面的程式碼全部整理之後,我們可以放到一個單獨的類中,這樣將測試指令碼和幫助處理其他功能的指令碼進行分離,這樣可以更加便捷我們維護測試程式碼。其次這樣寫出來的程式碼可讀性高,並且會隨著時間的增加,容錯性越來越強,最終將行成一個文件的UI自動化測試框架。

2,925 2 4