UiAutomator2.0升級填坑記
UiAutomator2.0升級填坑記
SkySeraph May. 28th 2017
Email:[email protected]
更多精彩請直接訪問SkySeraph個人站點:www.skyseraph.com
啰嗦
Google Android Developers 在2015年3月就發布了UiAutomator 2.0版本(下文簡稱U2),而公司的核心產品中用到還是UiAutomator老版本(下文簡稱U1),業界用U2的也不是很多,雖然有諸多問題和不便(如高版本OS中不支持Remote Debug等),但大家似乎在茍延殘喘中麻木了。 公司以前也有專家做過研究和探索,畢竟老項目是萬級別的,涉及諸多算法和復雜業務邏輯,遇到幾個問題最終不了了之。於是乎,又到了我手裏了。
重大特性
1. 基於 Instrumentation,使用Instrumentation test runner即可運行UiAutomator,反之,也即在基於Instrumentation的test中也能使用UiAutomator; 可以獲取應用Context,可以使用Android服務及接口。
2. 基於 Junit4,測試用例無需繼承於任何父類,方法名不限,使用Annotation進行; U1需要繼承UiAutomatorTestCase,測試方法需要以test開頭.
3. 與U1的Maven或Ant構建方式不同,U2采用Gradle進行構建; U2輸出為APK,Android工程,而U1為Java工程,輸出jar包。
4. 新增UiObject2、Until、By、BySelector等接口, 詳細請參考官方文檔。
其中,U2必須明確EditText框才能向裏面輸入文字,U1直接指定父類也可以在子類中輸入文字。
5. Log日誌輸出變更。U1可以使用System.out.print輸出流回顯至執行端,而U2輸出到Logcat。
6. 命令運行差異。
adb shell uiautomator runtest xx.jar -c package.name.ClassName adb shell am instrument -w -r -e class com.xx.xx com.xx.xx.test/ android.support.test.runner.AndroidJUnitRunner
代碼中運行:
Runtime.getRuntime().exec(“*“)
device.executeShellCommand(“*“)
命令格式:instrument [options] component
,詳細命令格式介紹參考官方文檔。
基礎使用
-
新建Android Studio工程。
-
gradle中添加依賴。
androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2’{ exclude group: ‘com.android.support’, module: ‘support-annotations’ }) androidTestCompile ‘com.android.support.test:runner:0.5’ androidTestCompile ‘com.android.support.test:rules:0.5’ androidTestCompile ‘com.android.support.test.uiautomator:uiautomator-v18:2.1.2’ androidTestCompile ‘com.android.support:support-annotations:25.3.1’
如需要依賴jar包
androidTestCompile fileTree(dir: ‘libs‘, include: [‘*.jar‘])
- androidTest目錄下添加測試用例。
@RunWith(AndroidJUnit4.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class XXTest { public Bundle bundle; public UiDevice device; private Instrumentation instrumentation; @BeforeClass public static void beforeClass() { } @Before public void before() { // Initialize UiDevice instance instrumentation = InstrumentationRegistry.getInstrumentation(); device = UiDevice.getInstance(instrumentation); bundle = InstrumentationRegistry.getArguments(); assertThat(device, notNullValue()); } @After public void after() { L.i(TAG + "After"); } @AfterClass public static void afterClass() { L.i(TAG + "AfterClass"); } public Bundle getParams() { return bundle; } }
采坑記
-
編譯錯誤.
這個應該說是官方故意坑爹吧,U2的包名結構發生了變化,所以需要將之前所有用到的U1相關API包名進行替換,具體為com.android.uiautomator.core. -> android.support.test.uiautomator.
-
反射相關.
這個有點坑爹,還是U2包名結構問題(原項目很多地方用到了反射獲取U1相關類/方法等),除非對原業務非常熟悉,否則只有遇到問題分析時才會發現此坑,讀者以後如果遇到這種類似包結構變化的問題,先不管三七二十一,全局搜索相關字段全局了解再說。當然,如果用到了混淆,也要對應修改。 -
UiAutomatorTestCase相關.
問題:去除所有測試類中的extend UiAutomatorTestCase後異常。因為原項目依賴於UiAutomatorTestCase中的getPara API來獲取U1命令傳參。
方案:修改成U2的InstrumentationRegistry.getArguments方式。 -
日誌相關.
問題:原項目中業務邏輯依賴System.out.println日誌輸出結果,而U2中System.out.println不會回顯到終端顯示,直接影響之前業務邏輯的執行。
方案:使用Instrumentation.sentStatus自定義狀態。 -
屬性文件.
描述:這個有點意思,原項目為Java工程,有很多屬性配置在properties屬性文件中,大概有20來個properties文件。而對屬性文件的讀取是在一個common的公共jar包中,采取的是Thread.currentThread().getContextClassLoader().getResourceAsStream方式。
問題:①屬性配置文件無法打包進apk中 ②讀取方式問題
解決:
方法1: 將屬性配置文件全部放入assets目錄,然後通過Context().getAssets().open方式讀取;而原先的jar中的公共類,抽離出來作為業務類。
方法2: 通過gradle自定義打包,將所有屬性配置文件打包進apk中,建議。 -
xml配置文件.
問題:原工程用到了RPC框架,有xml配置文件,存在與上述properties類似問題。
解決:gradle自定義打包。 -
權限問題.
問題:Android M+對權限進行了升級,本工程需要用到文件存儲權限,僅在Manifest中聲明WRITE_EXTERNAL_STORAGE權限還不夠。
解決:
方法1:
@Before public void grantPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getInstrumentation().getUiAutomation().executeShellCommand("pm grant "+getTargetContext().getPackageName()+"android.permission.WRITE_EXTERNAL_STORAGE"); } }
方法2:通過Rule
@Rule public final PermissionsRule permissionsRule = new PermissionsRule( new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE});
- NoClassDefFoundError問題.
問題:在Android 4.4手機出現NoClassDefFoundError問題,Instrumentation消息為
INSTRUMENTATION_RESULT: longMsg=java.lang.NoClassDefFoundError: org.junit.runner.manipulation.Filter$1
解決: MultiDex手動拆包A
①gradle文件中添加multiDexKeepProguard
defaultConfig { multiDexEnabled true multiDexKeepProguard file(‘multidex.pro‘) }
②multidex文件中添加
-keep class android.support.multidex.** {*;} -keep public class org.junit.runner.** {*;}
- 系統簽名問題.
描述:此問題是後續測試中才發現的,原來的穩定性/兼容性測試上的性能指標參數大部分顯示為空,極其詭異,後面花了很大功夫進行分析發現,很多指標會出現會出現類似Permission Denial : cannot dump 錯誤,獲取不到任何數據。因為原工程需要獲取電池、CPU、內存等等幾十項性能參數指標,有很大一部分是通過sh讀取系統文件等方式獲取的,通過Runtime.getRuntime().exec 或者 ProcessBuilder.start方式, 比如通過dump中的dump battery獲取電池信息,因為dumpsys涉及android:protectionLevel=”signatureOrSystem”,需要 權限,需要聲明android:sharedUserId=”android.uid.system”,需要系統簽名. …
解決:
①系統簽名(無奈之舉).
②采用device.executeShellCommand,註意只支持API 21+.
REFS
-
Google 官方文檔
-
Google 官方Demo
-
How to manage Runtime permissions android marshmallow espresso tests
-
設置Multidex出現java.lang.NoClassDefFoundError
-
Android Espresso multidex fail
後記
本文首發於skyseraph.com:“UiAutomator2.0升級填坑記”
同步發表/轉載 cnBlogs / …
By SkySeraph-2017
UiAutomator2.0升級填坑記