移动测试开发 黑盒测试中-关键截图-的打点方案

opentest-oper@360.cn · 2020年08月28日 · 1101 次阅读

Android 黑盒测试过程中如何进行有效的打点是我们经常遇到的问题,我们一般会在脚本内部进行数据打点,也可以使用其他进程录屏或截图。那我们如何选取合适的方式进行打点记录呢?下图是对常用打点方式的统计!对于测试开发人员来说有效的关键截图信息是最直观的数据,可以很快定位问题场景!本文重点介绍如何在 Shell 进程内统计屏幕截图变化。

第三方服务截图

本方案可以在 uiautomator1.0 脚本内实现,也可以自定义 Shell 进程服务来实现。个人推荐自定义 Shell 服务,然后用 app_process 命令启动。即使脚本出现异常退出了也不会影响截图服务,并且可以截取脚本启动前和结束后的状态。最主要的是 Android R 开始不再支持 uiautomator1.0 脚本。

主要通过 Activity 监听、定时器、进程监听服务触发截图操作:在页面变化时、进程状态变化时以及定时检测页面状态。除此之外我们需要一个 App 提供图片相似度的比较,或者搭建其他图片匹配服务也可以。

Activity 变化和应用异常时截图

通过 ActivityController 监听可以在 Activity 变化时获取对应的事件。但是进程 A 先初始化了 Activity 监听服务,我们再启动进程 B 初始化 Activity 监听,那么 A 进程的监听服务就被中断了,并且不会有任何提示信息。下面我们先介绍 ActivityController 监听 Activity 的方案:

一、首先我们要注册 ActivityController

1、直接通过 IActivityController.Stub() 方法获取 ActivityController 对象;
2、通过 ActivityManager 的 setActivityController 方法设置监听,设置成功后切换页面时就会回调对应的方法;

二、我们可以在 ActivityController 的回调中记录关键截屏信息,主要信息有:

1、新打开的 Activity:点击按钮跳转页面时;
2、恢复显示的 Activity:按返回键退回到上一级页面时显示的页面;
3、App 产生崩溃的详细信息以及截图;
4、App 产生 ANR 时的详细信息以及截图;
5、我们可以在 activityStarting 和 activityResuming 方法中对打开的页面进行控制。

三、缺点

1、如果其他进程也注册这个服务,那么会相互影响,只有最后注册的服务有效;
2、当在页面中切换 Fragment 时无法感知到事件变化;
3、必须在 Shell 进程内执行,普通 App 内是无法注册成功的;

IActivityController 相关方法解释:

IActivityController mActivityController = new IActivityController.Stub() {
            //当调用Activity的onCreate方法(跳转新页面)时回调此方法,
            //return false时不会启动Activity;return true时正常启动Activity
            //不要在这个方法内做耗时操作
            @Override
            public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
                return true;
            }
            //当调用Activity的onResume方法(比如dialog消失)时会调用这个方法
            @Override
            public boolean activityResuming(String pkg) throws RemoteException {
                return true;
            }

            /**
             * 有应用在Java层产生异常时回调此方法
             * 返回true时,会显示应用crash的弹窗,返回false时会立即kill应用
             */
            @Override
            public boolean appCrashed(String processName, int pid, 
                String shortMsg, String longMsg, long timeMillis, 
                String stackTrace) throws RemoteException {
                return false;
            }

            /**
             * 检测到ANR异常时就会回调此方法
             */
            @Override
            public int appEarlyNotResponding(String s, int i, String s1) throws RemoteException {
                return 0;
            }

            /**
             *  应用产生ANR时回调此方法,
             * Return 0 时显示 "应用停止响应" 弹窗。
             * Return 1 时不做任何操作,等待应用恢复正常;
             * Return -1 时结束产生ANR的应用.
             */
            @Override
            public int appNotResponding(String processName, int pid, 
                String processStats) throws RemoteException {
                return -1;
            }

            /**
            * 系统停止响应时回调此方法,可以在这个方法里面对设备进行些恢复工作         
            */
            @Override
            public int systemNotResponding(String msg) throws RemoteException {
                return 0;
            }
        };

进程变化监听

由于 ActivityController 服务存在被干扰的情况,我们需要其他服务进行辅助判断,通过 ActivityManager 注册 IProcessObserver 可以获取进程变化的回调,特别是在 onForegroundActivitiesChanged 方法内可以判断是否触发页面切换。

通过 IProcessObserver 可以实现的功能:

1、在 onForegroundActivitiesChanged 方法内监听页面变化;
2、在 onProcessStateChanged 和 onProcessDied 方法内可以统计 App 的线程生命周期;
3、可以统计 app 运行期间创建的线程信息;
4、该服务被不同进程注册时,相互间互不影响;

缺点:

1、没有直观信息,需要使用 uid 进行关联,处理复杂;
2、只有进程变化的简要信息,需要进一步加工才能获取我们想要的信息

IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
    //与用户交互的Activity发生变化时foregroundActivities为true,否则foregroundActivities为false
    //uid产生该活动的应用标识,可以用包信息匹配到具体的应用
    //pid 当前活动的线程/进城ID
    @Override
    public void onForegroundActivitiesChanged(int pid, int uid,
         boolean foregroundActivities) throws RemoteException {
        if(foregroundActivities) {
        //
        }
    }

    //活动状态发生变化时调用,在此方案内实际意义不大
    @Override
    public void onProcessStateChanged(int pid, int uid, int importance) 
        throws RemoteException {
    }
    //结束时调用
    @Override
    public void onProcessDied(int pid, int uid) throws RemoteException {
    }
};

综合应用

上面两种方案各有优劣,我们可以将他们整合在一个流程内实现较为完善的截图机制:
1、首先我们的服务主要依赖于 ActivityController 进行页面变化的感知,以便于获取详细的信息和精确控制;
2、我们注册 IProcessObserver 进行辅助验证,当页面发生变化时我们进行截屏,并通过 ActivityController 最后一次接收事件的时间判断其服务状态,如果服务状态异常,那么我们重新注册 ActivityController 服务;
3、开始监控页面时我们初始化一个定时器,可以定时(2S)截取屏幕,将当前截图 Bitmap 与上一次的截图 Bitmap 做图像匹配,如果图片相似度小于 0.8,那么可以认定页面发生了变化,如果 5S 内没有保存过页面信息,那么需要重新初始化 ActivityController 服务。

Root 设备的特殊方案

Android 系统是基于 Linux 系统的移动操作系统。它们可以通过 getevent 命令输出键盘和屏幕事件,包括响应事件的坐标区域和键盘 key 值。但是 Android 系统基于安全考虑,大部分真机系统是没有权限执行 getevent 命令的,它不会输出任何有效信息,但是华为手机(荣耀 20 i)上却可以正常使用。这是一个非常危险的权限,大家自己的手机还是不要开启开发者模式了!

下面是点击屏幕时该命令的输出:getevent -lt

/dev/input/event2: EV_ABS       ABS_MT_POSITION_X    0000024b
/dev/input/event2: EV_ABS       ABS_MT_POSITION_Y    0000065d
/dev/input/event2: EV_ABS       ABS_MT_PRESSURE      000000f5
/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   00000000
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MAJOR   000000a5
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MINOR   00000087
/dev/input/event2: EV_ABS       ABS_MT_ORIENTATION   ffffffc6
/dev/input/event2: EV_ABS       ABS_MT_BLOB_ID       00000002
/dev/input/event2: EV_SYN       SYN_MT_REPORT        00000000
/dev/input/event2: EV_KEY       BTN_TOUCH            DOWN
/dev/input/event2: EV_SYN       SYN_REPORT           00000000
/dev/input/event2: EV_ABS       ABS_MT_POSITION_X    0000024b
/dev/input/event2: EV_ABS       ABS_MT_POSITION_Y    0000065c
/dev/input/event2: EV_ABS       ABS_MT_PRESSURE      00000067
/dev/input/event2: EV_ABS       ABS_MT_TRACKING_ID   00000000
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MAJOR   000000b4
/dev/input/event2: EV_ABS       ABS_MT_TOUCH_MINOR   000000a5
/dev/input/event2: EV_ABS       ABS_MT_ORIENTATION   ffffffc1
/dev/input/event2: EV_ABS       ABS_MT_BLOB_ID       00000002
/dev/input/event2: EV_SYN       SYN_MT_REPORT        00000000
/dev/input/event2: EV_SYN       SYN_REPORT           00000000
/dev/input/event2: EV_SYN       SYN_MT_REPORT        00000000
/dev/input/event2: EV_KEY       BTN_TOUCH            UP
/dev/input/event2: EV_SYN       SYN_REPORT           00000000

ABS_MT_POSITION_X 屏幕的横坐标,后面一列是坐标的十六进制数据
ABS_MT_POSITION_Y 屏幕纵坐标,后面一列是坐标的十六进制数据
BTN_TOUCH 代表屏幕事件,后面如果是 DOWN 则代表按下,后面如果是 UP 代表抬起

我们可以根据 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 信息和 BTN_TOUCH 为 DOWN 的事件计算响应按下事件的屏幕位置,然后通过 BTN_TOUCH 为 UP 的事件计算抬起的屏幕位置,然后用按下和抬起的坐标差来判断该时间是点击还是滑动,然后我们可以进行屏幕截图,甚至在截图上绘制事件的轨迹。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册