入職兩月有余,從之前的android app開發到現在的測試框架開發,工作中遇到很多問題,趁這次機會分享一下。
Android自動化測試目前可借鑒的經驗不多,現在采取的方式就是通過java代碼對Activity和View進行操作,目前已知的入口是Instrumentation類。
Instrumentation與Activity均位于android.app包下,這個包內還有諸如ActivityManagerNative這種不對App層開放的類,通過查看Android源碼發現Activity類中諸如startActivity(Intent intent)這樣重要的方法都是通過Instrumentation實現,Instrumentation中也提供了一系列對Activity生命周期控制的方法。以Instrumentation為基礎,Android SDK在Junit基礎上進行了擴展,提供了AndroidTestCase類及系列子類,其中最重要的一個類是ActivityInstrumentationTestCase2。
基于Instrumentation的測試框架的工作原理SDK中的這張圖說明的很清楚了:
研究Android源碼發現框架層中有很多對測試有幫助的類、方法都被加上了@hide注解或是聲明為private的,無法從app層訪問。自然而然我們想到了java的反射機制, java反射允許我們訪問這樣的類和方法。
在上面的基礎上,國外有人開發出了robot/" target="_blank" >Robotium工具,可以在有app源碼或apk的情況下進行自動化黑盒測試。
但是Robotium目前的缺點也很明顯,無法對WebView進行操作,這對大量使用WebView的淘寶Android客戶端來說無疑是很大的限制。
而且Robotium提供的API是面向過程的,測試代碼的可擴展性差。
我們需要一個面向對象的,可對WebView進行操作的自動化測試框架,這就催生了TMTS(Taobao Mobile Test Studio)框架。
TMTS立項時還試圖著重解決另一個問題,就是Instrumentation框架下testapp和app運行在一個進程中 ,app crash會導致testapp一并crash。當時和士敦一起研究了Instrumentation、Activity的啟動流程,甚至想去研究一下dalvik是如何解析Manifest文件的,最后也沒有想到好的方法,收獲就是了解了android更底層一些的細節,這個問題現在先擱置了起來。
從測試代碼方面來看, Robotium中采用的是actionMethod(View, arg)的方式,TMTS中采用getView(id).actionMethod(arg)的方式,更加符合java的編程習慣。TMTS測試代碼的編寫也就是分三步,找到View,調用View的相應的action方法,斷言。
TMTS框架主要思想就是通過反射機制調用Android框架層API拿到當前Activity的所有View,在此基礎上返回需要獲得的View對象,對獲得的View通過Instrumentation封裝一些此View常用的操作,最后返回,這就是TmtsView及其子類。
這種方式缺點也很明顯,對每個從android.View繼承來的子類,如果其中有特殊的操作,就需要封裝出一個對應的TmtsView子類。
還有一個缺點就是目前是通過View在布局文件中聲明的id去尋找,這樣測試人員在編寫代碼時需要對app的源碼非常熟悉,了解當前操作的view的id是多少,在傳遞id參數時還有可能寫錯。之后我們對這個方式進行了一些改進,使用SDK自帶的hierarchyviewer工具獲得view的id;對每個布局文件進行解析生成java類,這個類中會提供方法返回布局文件中的所有帶id的view,經過討論,最后按view子類型來對一個布局中的view進行歸類。
現在測試代碼從getView(id).actionMethod(arg)演變成了Layout(layout.class).ViewType().view().actionMethod(arg)的方式,代碼雖然變長了,但是出錯的可能性大大降低了。
Bug的定位離不開日志,因而日志系統也是一個測試框架重要的組成部分,Android的Log類中提供了一系列的靜態方法可以在IDE中打印日志。在TMTS中,提供TmtsLog類,除日志打印外可將日志內容實時保存至SD卡指定目錄,在框架代碼中的關鍵部位都加上了這樣的日志用來保存異常時的調用棧信息,用戶的測試代碼中也可以加上對TmtsLog的調用跟蹤測試代碼執行進度,TmtsLog將為每個測試類保存一份這樣的日志文件,同時包含用戶的過程日志和框架異常日志,文件名以精確到毫秒的日期加以區分。
項目做到這里遠遠沒有結束,套用屈原的一句話就是路漫漫其修遠兮。
后面計劃解決的問題有:
1.跨進程測試,讓testapp和app運行在兩個不同的進程中,這是一個大坑。
2.穩定性問題,目前框架中有很多地方硬編碼Thread.sleep()去等待一個View加載完成,避免對空的View進行操作,或者是對一個view進行set操作后,也需要等待一段時間讓操作生效。希望能找到一種回調機制優雅的解決。
而且Robotium提供的API是面向過程的,測試代碼的可擴展性差。
我們需要一個面向對象的,可對WebView進行操作的自動化測試框架,這就催生了TMTS(Taobao Mobile Test Studio)框架。
TMTS立項時還試圖著重解決另一個問題,就是Instrumentation框架下testapp和app運行在一個進程中 ,app crash會導致testapp一并crash。當時和士敦一起研究了Instrumentation、Activity的啟動流程,甚至想去研究一下dalvik是如何解析Manifest文件的,最后也沒有想到好的方法,收獲就是了解了android更底層一些的細節,這個問題現在先擱置了起來。
從測試代碼方面來看, Robotium中采用的是actionMethod(View, arg)的方式,TMTS中采用getView(id).actionMethod(arg)的方式,更加符合java的編程習慣。TMTS測試代碼的編寫也就是分三步,找到View,調用View的相應的action方法,斷言。
TMTS框架主要思想就是通過反射機制調用Android框架層API拿到當前Activity的所有View,在此基礎上返回需要獲得的View對象,對獲得的View通過Instrumentation封裝一些此View常用的操作,最后返回,這就是TmtsView及其子類。
這種方式缺點也很明顯,對每個從android.View繼承來的子類,如果其中有特殊的操作,就需要封裝出一個對應的TmtsView子類。
還有一個缺點就是目前是通過View在布局文件中聲明的id去尋找,這樣測試人員在編寫代碼時需要對app的源碼非常熟悉,了解當前操作的view的id是多少,在傳遞id參數時還有可能寫錯。之后我們對這個方式進行了一些改進,使用SDK自帶的hierarchyviewer工具獲得view的id;對每個布局文件進行解析生成java類,這個類中會提供方法返回布局文件中的所有帶id的view,經過討論,最后按view子類型來對一個布局中的view進行歸類。