【Android測試】UI自動化代碼優化之路

關於UI自動化的抱怨html


  聽過很多人這樣講 「UI自動化很是不穩定,需求一改,界面一遍,所有都費了」。我相信作過的人可能也會有同感。既然這個問題一直都是存在的,那麼爲何沒有人仔細分析緣由呢?框架

  個人老闆george曾舉了這樣一個例子:每當需求變化的時候,開發沒有跳起來,反而是測試跳了起來。而後不斷的抱怨,界面元素全都改了,個人自動化的用例所有都要廢棄掉了。那麼咱們是否想過,爲何開發能夠從容不破的應對產品不斷變化的需求?而咱們卻不能呢?異步

  

  業內很多人也都放棄了UI自動化,以爲接口測試纔是最有意義的,的確接口測試相對於UI自動化的來講,確實穩定的多,可是難道咱們測完接口後,就不須要再測試UI了嗎?UI層但是最貼近用戶,也就是直接給用戶最直接的一層,咱們真的要放棄嗎?換句話說,若是客戶端開發在分層作的特別差的時候,客戶端的接口測試真的會那麼好作嗎?按下一個home鍵後再回來,onStop、onResum等一些生命週期裏的函數恐怕都很難應付的過來吧。說了這麼多,仍是回到正題吧,講一講如何寫好咱們的UI自動化測試工程。(注意,這裏是工程,不是腳本,腳本是很隨意的,隨便寫兩句用來測試的,工程就表明是一個能夠交付給用戶使用的,咱們能夠認爲開發的代碼是咱們要交付給用戶的產品,固然咱們的測試代碼一樣也是要交付給用戶的產品,因此後面統一叫測試工程)函數

 

 

簡陋的測試代碼工具


  首先來看看以前咱們的UI自動化的代碼是怎麼寫的:佈局

  

  這裏我使用了Espresso+Uiautomator2框架來進行舉例,忽略語法,只說結構的話是否是看上去很熟悉,你們在起初寫測試代碼的時候應該也是這樣的一個方式吧:在測試函數中完成全部的操做實現,若是是通用的操做,可能去封裝一個函數,固然有的時候還會用到setup進行初始化,使用teardown來進行收尾。我想說若是是這樣的寫法的話,咱們的確很難應對變化的需求和界面了,恐怕測試框架的更新換代,都能讓咱們的測試代碼所有推翻重寫。其實綜合起來咱們以前的代碼存在這樣的一些問題:post

  一、用例層包含了太多和源碼相關的內容,只要源碼改動,用例內容必改。測試

  二、用例層直接調用原框架內容進行實現,沒法處理一些通用的異常狀況,也不能適應工具框架的變化。優化

  三、通用的一些方法(例如登陸),多個用例集(**Test類)中的用例(test**()函數)可能都須要複用,須要處理好調用的方式。.net

  四、啓動Activity這樣的操做沒必要要每一個用例集都寫,能夠優化一下,抽象出基類。

  所以針對這些地方,咱們須要進行優化,咱們接着往下看。

 

 

關鍵字驅動


  yoyo(GT的開發)建議我使用這樣的一套用例編寫的方案,就是基於關鍵字驅動的方案,在用例層的實現剝離和源代碼相關的部分,使用高度抽象。這樣的話只要業務的流程不變,界面任意怎麼修改,咱們都不須要去修改用例層的代碼。整個框架的實現結構以下:

   

  這裏若是你們看類圖有些吃力的話,能夠先看看UML類圖介紹。這裏先大體說下這些類模塊的做用:

  BaseTest:全部Test類的基類,也就是測試用例的基類,裏面實現ActivityTestRule來啓動Activity,若是有須要的狀況下能夠實現BeforeClass和AfterClass,這兩個在整個命令的運行週期內只在開始和結束的地方執行一次。

  用例Test:具體的測試用例的實現類,這個能夠理解爲一個測試集,每一個類中有若干test函數,每一個函數就表明一個測試用例,用例的寫法採用關鍵字驅動的方法。

  Key:用枚舉定義着全部的關鍵字。

  Command:接口類,供Word實現execute(Obj)的方法。

  FrameCommand:基類,供Word層來繼承,裏面只封裝了一個execute(Key, Obj...),主要用在AW中調用KW的實現;這裏須要注意和Command接口中execute的區別。

  Word層:即圖中Login、Enter**Page等,須要實現Command接口中的execute函數,同時繼承自FrameCommand,解釋爲何是Word層,這裏須要把這些實現抽象成ActionWord(簡稱AW)、KeyWord(簡稱KW),而這二者的區別就是,KeyWord中實現可複用的一些場景,ActionWord中能夠包含KeyWord,實現一些不多被複用的場景。

  TestContext:將Key中關鍵字和具體的Word實現的函數進行關聯,構造一個map,使得直接經過execute關鍵字就能調用起對應的函數。

  具體實現的代碼我先不詳細解釋,咱們先來看看使用這套框架後,以前實現一樣功能代碼寫成了什麼樣子:  

  

  能夠看到,測試用例(這裏認爲一個test***函數就是一個測試用例)這一層咱們作了高度的抽象,在testPublish這個函數中沒有任何與開發源代碼或者是資源id有關的信息了,這裏的Key.EnterPublishPage就是咱們的關鍵字,具體的實如今EnterPublishPage這個AW的函數中,這樣寫用例,當咱們的界面發生了大的改變,例如咱們版本迭代中從發佈的兩個頁面,以下所示:

  

  在新版本改變後,改爲了一個界面,以下圖:

  

  能夠看到界面元素的調整仍是很多的,若是以前咱們可能就得廢棄以前的用例從新寫了,可是使用了關鍵字驅動後,咱們的用例層的改變根本不須要作任何的修改,而對應的若是控件ID改變後,咱們只須要修改Word層便可。

  這裏說下AW和KW之間如何封裝,就代碼層面,對於編譯器來講,是沒有AW和KW之分的,咱們抽象這兩層的意思就是,當有一個Word能夠被多個用例複用的話,這樣咱們就把它封裝起來供其餘的Word使用。具體還須要在實踐過程當中慢慢的體會。  

 

 

封裝測試框架


  說完了關鍵字驅動,須要說下封裝框架這一塊。看過我以前文章的朋友們應該知道我爲何要選擇Espresso和Uiautomator,目前谷歌推薦使用這兩款框架,有興趣能夠看看谷歌的這兩篇原文:

  

  那麼兩個框架同時加入到咱們的測試工程應該如何去整合代碼結構內,這裏我本身使用這樣的結構,以爲還不錯的能夠參考一下,首先先看下類圖:

  

  看完類圖後可能有些人已經看出來啦,沒錯這裏使用了簡單工廠模式,具體的Word層來使用工程加工出來的對象,具體的工具封裝內容包裝在FrameUiautomator和FrameEspresso裏。

  這裏主要說下針對Uiautomator的封裝,若是以前讀過我寫的《如何組織好你的測試代碼》,那麼裏面的一些思想應該比較清楚了,主要的優勢就是:

  一、頁面跳轉或者異步加載延遲出現的界面,無需再單獨使用sleep;

  二、對於系統隨機出現的可能會影響App界面的一些因素(例如Android6.0的受權彈框、電話呼入),無需再單獨處理;

  三、對於App中隨機出現的可能會遮擋正常界面的一些彈框,無需再單獨處理;

  四、全部調用封裝後框架的操做,都會記錄日誌;

  五、框架自己有斷言能力,若是在框架處理異常狀況後還找不到指定控件,這時候會截圖而且斷言;

  六、若是須要替換框架或者框架升級,可使用最小的成原本框架層進行改動,而不須要改動用例層和Word層。

 

 

完善其餘內容


  上面主要講的是UI自動化的一些行爲操做,關於斷言的問題,我這裏不想說太多,BTV作到界面上的UI元素的檢查,以及整個流程是否能夠完整的走下來就能夠了,若是須要驗證數據正確性等一些複雜的內容,能夠參考我寫的《App任你擺佈(反射技術的引入)》。我這裏說說UI自動化若是失敗了,咱們怎麼排查問題?其實很簡單我這裏作的就是日誌+截圖。

  日誌系統最關鍵的是打日誌的時機,這裏我把它埋到了BaseTest的execute()中,這樣每一次的用例調用AW或者AW中調用KW,均可以記錄下來,同時也埋到了框架的具體實現函數中,這樣框架只要操做就會記錄下日誌。這裏有個小的技巧,我在打印日誌的地方調用了下面的函數:

String classname = Thread.currentThread().getStackTrace()[3].getClassName();

  這樣就能夠記錄下當時調用Log函數的當前的類的名稱了,這裏能夠看下我輸出的log的樣子:

  

  是否是比較齊全了,基本上全部你想知道的信息均可以經過log內容來得到了。下面說說截圖,截圖和log的總體思路同樣,會在一些關鍵節點埋點同時也支持手動調用。由於個人工具框架是支持自身斷言的,所以我在工具框架這一層斷言的時候會加入截圖,其餘地方若是你須要特別關注的時候,也能夠手動調用截圖觸發。

  

   上面的圖是否是很是直觀,當咱們的用例出現異常錯誤的時候,直接經過log和日誌便可定位到問題的所在。

 

 

結果展現


  測試結果最終對接了內部的持續集成平臺和結果展現平臺後是這個樣子:

  

  保證了編譯器中的結果和結果展現平臺中顯示的狀況一致。 

 

 

結合實踐談談


  互聯網產品的迭代速度之快,各位都深有體會。作爲產品質量的保障者,測試人員常常爲測試時間不足而煩惱,如何打破現狀來讓如今變得更好一些,這是咱們一直在思考的問題。軟件工程中有提到測試人員越早的介入到研發的流程當中,就能夠越早的發現問題,從而下降發現問題的成本。所以"左移"變得很是的有必要了起來,固然左移的方式有不少,例如前幾天拜讀到的《聊聊測試「左移」那些事》這裏面主要講測試人員經過把控需求來達到左移的效果,而我上面所講到的內容均可以幫助自動化實現左移的。

  想一想以前咱們作的UI自動化是怎麼作的呢?在版本提測以後,咱們開始寫自動化,這樣自動化的主要功能就變成了迴歸和冒煙。我這裏我想說的是在開發寫代碼的時候,咱們也開始寫用例級別代碼,在開發定義了界面佈局後,咱們就能夠完善具體代碼,待開發提測時,咱們就能夠運行咱們的用例來進行測試了。

  固然針對新老版本可能略微有所不一樣:

  若是是新需求的狀況下,咱們在需求肯定的狀況下就能夠先組織本身的用例了,具體實現依賴開發的word層的代碼能夠先空着,待開發肯定以後,咱們就能夠及時的完善咱們的word層,這樣不用等到開發提測以後,咱們纔開始設計咱們的自動化測試用例。

  對於老的需求變動,一樣也是,首先能夠看以前的用例中的關鍵字是否有可複用的東西,若是能夠直接複用,那就繼續用,若是有新的步驟加進來,那麼只須要加入對應的關鍵字便可,和新需求的作法同樣,一樣在開發提測以前完成用例的編寫。

  其實在整個方案啓動以前,我就在思考這個問題。那麼這個作出來後究竟會有收益嗎?由於畢竟作完整個系統不是一件容易的事情,須要花不少的時間成本進去。那麼分析收益從哪方面入手呢?我覺定先從bug入手,因而針對最近的一次版本作了一個簡單的bug分析:
  
  從數據中能夠看到,的確有一部分的bug是能夠在左移階段被發現的。這裏分爲BVT級別的用例和詳細模塊的用例。BVT級別用例來限制開發的提測,提測前開發本身去運動這部分用例,經過才能夠提測;具體功能級別的詳細模塊的內容用專門針對這個版本修改或者新增的新功能。

  這就是本節想跟你們說的內容,整個過程當中無論是對項目的收益或者是本身的成長,我都有所收貨,拿出來和你們進行分享,但願能幫助到其餘人。

 
分類:  軟件測試
相關文章
相關標籤/搜索