zoukankan      html  css  js  c++  java
  • UiAutomator2.0升级填坑记

    UiAutomator2.0升级填坑记

    SkySeraph May. 28th 2017

    Email:skyseraph00@163.com

    更多精彩请直接访问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. 新增UiObject2UntilByBySelector等接口, 详细请参考官方文档
    其中,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

    后记

    本文首发于skyseraph.com“UiAutomator2.0升级填坑记”
    同步发表/转载 cnBlogs / …



    By SkySeraph-2017

  • 相关阅读:
    Java异常处理机制(转)
    深入探讨 java.lang.ref 包(转)
    一篇不错的讲解Java异常的文章
    Java国际化学习(一)介绍
    Java泛型集合排序(转)
    Java常见异常总结 (转)
    java中的异常处理机制
    深入分析 Java I/O 的工作机制(转)
    java里BufferedReader和Scanner
    JS深入学习知识整理
  • 原文地址:https://www.cnblogs.com/skyseraph/p/7297450.html
Copyright © 2011-2022 走看看