在整個自動化測試架構中,單元測試爲最基礎也是效率最高的層次,如圖1所示,單元是整個軟件的構成基礎,像硬件系統中的零部件同樣,只有保證零部件的質量,這個設備的質量纔有基礎,單元的質量也是整個軟件質量的基礎。java
圖1自動化測試架構及效率算法
在肯定單元測試框架設計前,先作了單元測試技術的調研,對目前市面上開源的工具作了比較分析。數據庫
圖2和圖3爲兩個備選技術框架,選擇圖2的框架選型1做爲單元測試框架。安全
選型1選擇了Roboletric、Junit和Mockito框架,緣由爲Roboletric是以Java Junit的方式運行,脫離了對Android環境的依賴,能夠直接將case在JVM中運行,而Mockito用於在測試過程當中,對於某些不容易構造(如 HttpServletRequest 必須在Servlet 容器中才能構造出來)或者不容易獲取的比較複雜的對象(如 JDBC 中的ResultSet 對象),用一個虛擬的對象(Mock 對象)來建立以便測試的測試方法。服務器
Mock最大的功能是將單元測試的耦合分解開,若是你的代碼對另外一個類或者接口有依賴,它可以模擬這些依賴,並驗證所調用的依賴的行爲。圖4描述了某代碼模塊依賴關係,圖5爲使用Mock解耦後的代碼模塊依賴關係。網絡
圖2框架選型1 圖3框架選型2架構
圖4某代碼模塊依賴關係 圖5 Mock解耦後的代碼模塊依賴關係併發
選型2使用Robotium、Android Test Suite(Instrument),該套框架基於Android Runtime,必須在模擬器或真機上測試,效率比較低下,但因爲是Android SDK內置支持的單元測試和功能測試框架,其與系統的聯繫很緊密,測試環境很是接近真實環境,對某些特殊的測試需求,好比須要系統的特定功能屬性或模塊,使用選型2就比較合適。框架
Robotium基於Android Test Suite,提供了更容易使用的測試接口,主要側重黑盒測試。ide
表1描述了單元測試技術框架的橫向比較狀況。
表1單元測試技術框架比較
|
優勢 |
缺點 |
JUnit |
速度快、支持測試覆蓋率等代碼質量檢測工具 |
沒法作與Android UI相關的操做、與原生Java有差別 |
Mockito |
解除測試對象的依賴關係、驗證行爲等 |
沒法配置static、final等修釋的屬性或方法 |
Robolectric |
支持對Android平臺依賴類底層的引用和模擬(支持有限) |
沒法完成UI Layout的測試、啓動速度相對較慢,不利於單獨Unit Test的開發 |
Robotium |
緊密結合產品代碼測試,適合完成Activity粒度的真實場景下的單元測試 |
依賴安卓平臺、速度很是慢 |
Android Instrument |
可使用絕大多數Android控件和有比較好的設備層支持 |
速度很是慢、依賴平臺、須要模擬器或真機支持 |
主線代碼使用了與圖2框架選型1一致的單元測試框架,只有一點細微的差異,主線單測框架使用PowerMock,本方案採用Mockito,後查明PowerMock包含了Mockito的功能並作了加強,故最終肯定使用PowerMock替換框架選型1的Mockito。
移植後的編譯環境爲Gradle,須要導入圖6中的單元測試依賴包。
圖6單元測試所需依賴包
圖7描述了單元測試框架,該框架以主線單元測試框架爲基礎,書籤模塊單元測試用例爲模板設計而成。
Robolectric運行環境運行在JVM裏,具備可移植性,Junit、Mockito運行於Robolectric環境上方,專一於單元測試用例編寫,而Roboletric用於解耦單元測試與Android運行環境的依賴。
圖7單元測試框架
模塊基礎框架面向特定功能模塊,設計對該功能模塊的特定業務的單元測試架構,不一樣的功能模塊該模塊基礎設計可能不同,對應於不一樣業務的不一樣特性。
模塊單元測試用例是基於模塊基礎框架,編寫具體業務的單元測試用例。
根據文檔《XXX書籤雲同步測試用例_20170515.xmind》的順序小節,編寫了14個單測用例。目前測試結果:13個經過測試,1個與預期不符。
圖8爲單元測試代碼入口,用@Test標記的函數爲一個單元測試方法。
圖8單元測試代碼入口
圖9爲某個單元測試用例的代碼示例,使用assertEquals斷言case執行結果的正確性。
圖9單元測試用例
圖10爲單元測試執行結果,從圖中可知共14個case,1個failed,13個passed.
圖10單元測試執行結果
圖11顯示與預期不符的case執行結果。
圖11與預期不符的case執行結果
書籤排序部分的單元測試的目標模塊是獨立於其餘業務模塊和系統平臺的排序算法,實質上是純JUnit單元測試,沒有涉及到Robolectric和PowerMock框架,而項目引入單元測試的主要目的是模擬業務場景,並在這些業務場景下運行健壯性、安全性等方面的測試用例,從中發現代碼的問題,進而確保工程質量。
在Android項目下作單元測試,必然要基於Android平臺去作單元測試,目前單元測試的框架選定用Robolectric替代Android運行時環境,雖然Robolectric已經較多地應用於Android開發的單元測試,但具體應用到書籤雲同步模塊,該框架是否適用,應用過程會出什麼問題,這些仍須要作風險預妨,表如今實操中就是合理安排任務計劃,預留必定的時間應對可能出現的風險,同時在作單測方案時考慮層面更深、廣度更全面、方案須要作的更詳細和更容易落地等。
現階段的業務模擬,主要是模擬本地書籤的增刪改查和雲同步。
圖12描述了書籤雲同步的類圖,圖中粉色背景部分爲單元測試依賴的類,綠色部分爲華爲去服務SDK中的類。
從圖12中有兩個主要分支,一個爲以BookDaoManager爲首的本地數據庫管理類羣,另外一個爲以HwCloudSyncManager爲首的雲同步類羣。本地書籤的增刪改查是基於BookDaoManager去設計cases,同步測試是基於HwCloudSysncManager去設計cases。
圖12書籤雲同步類圖
圖13描述了模擬書籤雲同步業務的單元測試架構,JUnit的以@Test標記的Test method爲單元測試入口,在Test method中調用Robolectric的控制對象和影子對象,經過Robolectric去運行App中的UnitTestActivity,測試用例寫在TestActivity,這樣就實現了在JVM上單測Android業務模塊。
圖13書籤雲同步業務模擬單測架構
值得一提的是,在單元測試架構中,使用者只需關注AndroidInterface和AndroidInterfaceTest的編寫,無需關注Robolectric容器和具體App結合的原理和邏輯。
表1圖13中主要對象含義
對象 |
子對象 |
做用 |
JUnit |
Test Method |
單元測試入口,用於調用AndroidInterfaceTest中的方法。 |
… |
|
|
AndroidInterface |
Android Method |
待測方法,運行在Android Runtime中。 |
AndroidInterfaceTest |
Test Method |
專測AndroidInterface中的方法。 |
… |
|
|
UnitTestActivity |
|
待測方法運行的容器。 |
根據文檔《XXX書籤雲同步測試用例_20170515.xmind》的穩定性、邊界值等小節,編寫了13個單測用例。目前測試結果:13個經過測試,0個與預期不符。
圖14~圖15爲現已實現的用例。
圖14書籤基本增刪改查用例 圖15書籤穩定性用例
圖16書籤邊界值用例
圖17展現的是JUnit書籤數據庫操做單測入口。
圖17書籤數據庫操做單測入口
圖18爲穩定性測試用例中,書籤批量增刪改的用例代碼。
圖18書籤批量增刪改用例代碼
圖19爲書籤雲同步用例模板,全部的書籤雲同步用例均按該模板編寫,圖18中的批量增長、修改和刪除用例就是以該模板(BaseBookmarkDB)編寫而成。
模板(BaseBookmarkDB)分爲五個步驟:
第一步、使用Robolectric構建Activity,並經過ActivityController控制Activity的生命週期到指定狀態;
第二步、執行單元測試;
第三步、打印單元測試相關信息;
第四步、使用Robolectric銷燬Activity,經過ActivityController控制Activity進入銷燬的生命週期序列;
第五步、斷言單元測試結果。
圖19書籤雲同步用例模板
圖20爲書籤增刪改查相關用例的執行結果。
圖20書籤增刪改查用例執行結果
書籤雲同步須要調用華爲雲服務SDK將本地書籤數據同步給雲端服務器。用單元測試模擬同步業務,在軟件架構上遇到必定的問題,由於單元測試框架不包含華爲雲服務SDK,將華爲SDK導出而後放到單元測試框架也不必定能正常運行,因此現階段對同步業務的模擬,在設計上採用了一種間接的方式,即經過adb控制手機,將書籤數據經過adb傳送到手機,而後利用am start一個SyncActivity,該SyncActivity負責讀取書籤數據併發起同步請求,最後SyncActivity將同步結果保存到sdcard,PC端以輪詢的方式讀取sdcard存儲的同步結果。
圖21爲書籤雲同步拓撲圖,也是單元測試同步框架實際場景圖,工做人員將A設備經過USB線鏈接到電腦上,工做人員在電腦上運行測試用例。
圖21書籤雲同步拓撲圖
圖22描述了書籤同步單元測試框架的原理。該框架基於C/S架構,Client端運行在電腦上,測試用例跑在Client端,同時要實現一個SyncActivity做爲服務端跑在手機上。
圖22書籤同步單測框架原理
圖23爲同步單測框架原理流程圖。
目前同步單測框架已編碼完成,但沒有通過調試驗證執行結果的正確性及框架運行的穩定性。
圖23同步單測框架原理流程圖
原有書籤排序採用拓撲排序,時間複雜度爲O(v+e),在排序過程當中各有向邊的權值是時間參數,在和千興的溝經過程中瞭解到,算法並無很好地處理不一樣設備間時間參數的同步,從而有潛在的風險。所以,提出了基於時間同步的改進方案,即統一使用第三方服務器的時間,就可避免因時間參數未同步帶來的風險。
圖24描述了改進方案的設備時間同步流程。
圖25描述了改進方案在網絡恢復時的設備時間同步流程。
圖26描述了書籤雲同步時合併書籤數據流程。
該方案在雲同步時的排序時間複雜度爲O(m+n),其中m爲本地書籤個數,n爲雲端書籤個數。
該方案總的排序時間複雜度(書籤雲同步時的排序+同步第三方服務器時間後的排序)爲O(nlogn) 。
圖24設備時間同步流程
圖25網絡恢復時的設備時間同步流程
圖26書籤雲同步時合併書籤數據流程
在現有實現方案中,用戶增長、修改書籤(包括書籤之間的順序)和刪除書籤後,應用會當即將修改回寫數據庫,在用戶頻繁改寫書籤的場景下,會形成比較大的性能開銷,所以提出一個改進方案,即在用戶進入書籤管理界面後所做的書籤修改,應用只是修改內存中的數據,在用戶退出書籤管理頁面後,應用纔將內存中的書籤數據回寫數據庫,以此避免較多的IO開銷。
在某此極端狀況下,好比應用crash或應用被系統殺掉,致使內存中的書籤數據來不及回寫數據庫,應對這些極端場景,可進一步被充改進方案,在監測到用戶最近一次修改5分鐘後,將內存書籤數據回寫數據庫。
添加java library工程,配置好該libraray工程對junit、robolectric和powermock的依賴,如圖6所示,而後在build.gradle中添加一個copyJar task,copyJar task如圖27所示。
圖27導出Gradle jar包task
運行gradle copyJar,或在圖28中雙擊copyJar,單元測試框架依賴的jar包會保存到build/libs/lib目錄。
圖28 Gradle projects窗格執行copyJar
Ant內置支持JUnit構建,而Robolectric基於JUnit構建,加之PowerMock更傾向於中間件的角色,因此Ant天生支持JUnit + Robolectric + PowerMock組成的Android單元測試框架,準備工做只需將JUnit + Robolectric + PowerMock依賴的jar包放到libs目錄,並在build.xml配置好指向該libs的classpath。
圖29爲 Ant打包apk 有向非循環圖(DAG),粉色背景標識的任務爲單元測試任務,單元測試任務依賴於compile任務,若是要作單運測試,只須要運行ant junit便可。
另外,Ant引入單元測試框架,只需在原打包任務流的基礎上加上junit任務,並使junit任務依賴compile便可,並不會對原打包流程形成大的干擾。
圖29 Ant打包apk DAG
圖30和圖31爲Ant下運行單元測試後生成的結果報告。
圖30 Ant下單元測試報告
調試並驗證同步單元測試框架的正確性,而後增長健壯性、安全性、同步方面的用例編碼實現。
目前的單元測試框架僅應用於書籤雲同步模塊,並處於效果檢驗期,若是單元測試效果較明顯,項目後續還會將單元測試應用於各新增功能模塊的開發和維護。
單元測試框架搭建及書籤雲同步順序單元測試用例編寫,是本人第一次進入項目作的事情,先後涉及技術框架選型、主線框架移植、單元測試案例編寫、單元測試案例及測試結果校對等事項,由於暫時沒有代碼權限,目前僅對書籤雲同步的排序進行了單元測試用例編寫及測試,共14個單測用例,其中13個單測用例經過校驗,1個與預期不符,該單測用例緣由目前正在排查。
單元測試是很基礎,也是很重要的開發質量保證模塊,雖然開發人員大多數不肯意寫單元測試,可是從長遠來看,單元測試的做用是至關大的,體如今修改Bug後的代碼校驗、新人接手某功能模塊、模塊修改後的質量保證、最大化地減小軟件功能集成及發佈後的Bug發生機率。
雖然單元測試有不少優勢,但也有缺點,好比單元測試代碼每每是所測目標代碼的2~4倍的體量,通過時間的積累,單元測試代碼的維護是一個問題。