Robotium 基于 Robotium 的自动遍历方案——开源

Heyniu · 2017年02月13日 · 最后由 chihua 回复于 2019年03月04日 · 9735 次阅读
本帖已被设为精华帖!

前排提醒 :文章稍微长了点,文字占比也高,但是读完你会感觉花费的时间是值得的!

前言

​ 做这个的初衷是发现项目中的崩溃问题(即稳定性)。Monkey 达不到全覆盖,也试过思寒的AppCrawler,无奈速度上不太理想。我需要的是更快的反馈结果,于是乎着手自己写一个方案,也当做是提高编码能力,或者说对 Android 有更深入的理解。

解决了什么

​ 初期目标是想替代 Monkey,众所周知 Monkey 的随机点击,以及不可控性,并不能做到完整的遍历。所以当下最主要的功能是发现崩溃问题(如兼容性、混淆、代码问题导致的崩溃),额外可以做的是发现无数据时的空白布局(配合接口工具,启用快速模式验证)、发现无网络时是否显示无网络的布局(关闭网络,启用快速模式)等等。

使用效果

​ 在我们的产品上,启用爬虫模式试跑了几个小时发现了 5 个崩溃问题。当然发现第一个崩溃时自动遍历就停止了,它依赖于被测应用,被测应用崩溃,它也会一同退出,这是接下来要解决的问题(增加重启机制 )。当崩溃问题不予修复时,继续遍历,还是会走到第一个崩溃(可复现性 ),此时可以把崩溃的 Activity 加入忽略列表。

崩溃问题:

  • 发现的崩溃问题都是正常操作的,非异常操作(举例:非人类手速的点击等等)。换句话说就是用户也会遇到该问题

  • 擅长发现异步请求导致的崩溃问题

    • 异步请求拿到数据后更新 UI,由于 UI 被销毁导致的崩溃
    • 为什么说是擅长?
    • 遍历逻辑基于 Activity,点击 View 跳出本 Activity 后按返回键回到遍历 Activity
    • 当网络不稳定时,数据返回延时加长,View 销毁了数据才回来,如果此时代码没处理好就会发生崩溃
    • 建议遍历时切换到弱网环境

特性

  • 可跨应用

    • 补上 Robotium 不支持跨应用的短板
    • 自动遍历时不会有具体的跨应用操作,唯一出现的地方在 Android 6.0 以上版本启动应用时的授权操作(可能存在兼容性问题)
    • 跨应用应用在单独写用例时
    • 跨应用详情
  • 多种模式

    • 快速模式:只启动 Activity,快速检测崩溃问题(如兼容性、混淆、代码问题导致的崩溃),一般几分钟可完成。依赖于 Params.json 文件,该文件可由录制模式产生。
    • 迭代模式:启动 Activity 并点击每个 View。依赖于 Params.json 文件,该文件可由录制模式产生。
    • 爬虫模式:通过迭代主页并记录新开 Activity,迭代完毕后读取新开 Activity,循环往复,直至无新的 Activity。
    • 录制模式:需人工操作应用,记录每个新开的 Activity,供快速模式、迭代模式使用。录制模式可在功能测试阶段使用,录制模式默认休眠 1 个小时,期间操作应用打开的 Activity 都将被记录下来。
  • 智能输入

    • 根据输入框支持的输入类型和最大长度进行输入
    • 支持手机号、邮箱、普通文本等类型
  • 红点标记

    • 将要被点击的 View 会以红点标记保存为截图
    • 如果发现截图没有红点或者红点位置明显错误时,不用惊讶,那一定是隐藏的 View 被点击了
    • 没有红点:隐藏的 View 坐标不在屏幕范围
    • 红点标记错误:点击到被遮挡的 View,通常发生在 ViewPager 布局
  • 无惧遮挡

    • 被遮挡的 View 也可以点击到,因此无需滑动操作。
    • 举例:列表一次性加载 10 条数据,屏幕只显示了 5 条,剩下 5 条没有显示的也可以点击到。
  • 完全遍历

    • 应用所有 Activity 都可以遍历到,360°无死角。
    • 快速模式、迭代模式覆盖度最高可达 100%,通过爬虫模式 + 录制模式组合产生的 Params.json 文件,或单独录制模式产生的 Params.json 文件。
    • 爬虫模式亦可达到很高的覆盖度,不同应用覆盖度不一致,依赖 Activity 关联度。
    • 提高爬虫模式覆盖度的方法:采用划分模块的方法,比如主页模块、个人模块等等
  • 一触即达

    • 只需一步就能打开应用内任何 Activity
    • 举例:在已经登录的情况下,想去到登录页面,一般可能的做法是在主页点击我,去到个人中心,个人中心滑动到最底部,点击退出登录,来到登录页面。一触即达只要知道登录页面的名称、启动参数就能直接打开登录页面。
  • 可复现性

    • 在数据相对不变的情况下,遍历 Activity 中 View 的顺序是一致的,因此具备一定的复现可能性,可理解为 Monkey 中的种子
  • 多重跟踪

    • 多重跟踪能在出现崩溃的情况下,更好的定位、复现、分析问题。
    • 截图跟踪:每个点击操作都将被记录,根据截图顺序可知进行了何等操作
    • 日志跟踪:崩溃日志抓取,供研发使用
    • 接口跟踪:配合 Fiddler 等抓包工具,可知发生崩溃时请求了哪些接口,从而更好的定位问题
    • 元素跟踪:点击的 View 信息以操作日志形式记录在 SD card,包含包名、类名、资源 ID、屏幕位置、文本等等信息
  • 支持 Hybrid

    • 除了支持 Native 遍历,亦支持 Hybrid

技术细节(局部)

关于跳转的处理

  1. 每次点击后都会判断是否离开遍历 Activity(未离开则进入下一个点击事件)

  2. 如果跳转到本应用其他 Activity(则按下返回键返回,返回后回不到遍历 Activity 则重启该 Activity 并重新遍历剩余 View)

  3. 如果跳转到其他应用去了(如相机)则直接重启该 Activity 并重新遍历剩余 View

  4. 如果跳转到登录页面则登录后继续操作(可能存在遍历时点击到退出登录按钮)

关于直接启动 Activity 的处理

通过监听 Activity 的启动,拿到 Activity 实例并获取传入参数,看下流程图可能好理解:

配置说明

参数描述:

// Activity截图开关,默认为true。启动Activity首先会截取一张图保存在sdcard/AutoClick/Screenshots/Activities文件夹
public boolean activityScreenShots = true;
// Activity迭代截图开关,默认为true。每次点击View会截取一张图保存在sdcard/AutoClick/Screenshots/对应Activity文件夹
public boolean iterationScreenShots = true;
/**
         * 迭代模式
         快速模式:只启动Activity,快速检测崩溃问题(如兼容性、混淆、代码问题导致的崩溃),一般几分钟可完成。依赖于Params.json文件,该文件可由录制模式产生。
         迭代模式:启动Activity并点击每个View。依赖于Params.json文件,该文件可由录制模式产生。
         爬虫模式:通过迭代主页并记录新开Activity,迭代完毕后读取新开Activity,循环往复,直至无新的Activity。
         录制模式:需人工操作应用,记录每个新开的Activity,供快速模式、迭代模式使用。录制模式可在功能测试阶段使用,录制模式默认休眠1个小时,期间操作应用打开的Activity都将被记录下来。
         */
public Mode mode;
// 被测应用主页,必填项
public String homeActivity;
// 被测应用登录页,必填项
public String loginActivity;
// 被测应用登录账户,必填项
public String loginAccount;
// 被测应用登录密码,必填项
public String loginPassword;
// 被测应用登录页面登录按钮资源ID,必填项
public String loginId;
// 被测应用包名,必填项
public String PACKAGE;
// 忽略的Activities数组,此数组内的Activity不会遍历
public String[] ignoreActivities;
// 忽略的Views数组,此数组内的View不会遍历,需填写完整的资源ID,如com.xx:id/iv_fpc_back
public String[] ignoreViews;
// Activities截图保留开关,默认为true,如果为false,Activity遍历完成后,截图将会被清理,Activity发生崩溃时,截图不会被清理。
public boolean keepActivitiesScreenShots = true;

示例:

package application.iteration;

import android.test.ActivityInstrumentationTestCase2;

import com.robotium.solo.Solo;

import org.junit.After;
import org.junit.Before;


@SuppressWarnings({"rawtypes", "deprecation"})
public class Iteration extends ActivityInstrumentationTestCase2 {

    /**
     * 被测应用包名
     */
    private static final String PACKAGE = "被测应用包名";

    /**
     * 被测应用Activity入口
     */
    private static final String LAUNCHER_ACTIVITY = "被测应用Activity入口";

    private static Class<?> launcherActivityClass;

    static {
        try {
            launcherActivityClass = Class.forName(LAUNCHER_ACTIVITY);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public Iteration() {
        super(PACKAGE, launcherActivityClass);
    }

    private Solo solo;

    @Before
    public void setUp() throws Exception {
        Solo.Config config = new Solo.Config();
        // 遍历模式
        config.mode = Solo.Config.Mode.REPTILE;
        config.homeActivity = "被测应用主页Activity";
        config.loginActivity = "被测应用登录Activity";
        config.loginAccount = "登录帐号";
        config.loginPassword = "登录密码";
        config.loginId = "登录按钮ID";
        // 被测应用包名
        config.PACKAGE = PACKAGE;
        config.ignoreActivities = new String[]{"忽略的Activity,此数组中的Activity将不会被遍历"};
        config.ignoreViews = new String[]{"忽略的View,此数组中的View将不会被点击,需填入完整的资源ID"};

        solo = new Solo(getInstrumentation(), config, getActivity());
        super.setUp();
    }

    @After
    public void tearDown() throws Exception {
        solo.finishOpenedActivities();
        super.tearDown();
    }


    /**
     * 自动遍历入口
     * @throws Exception 抛出异常
     */
    public void test_iteration() throws Exception {
        solo.startIteration();
    }

}

AndroidManifest.xml 配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="被测应用包名.test"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-permission android:name="android.permission.GET_TASKS" />

    <uses-sdk 
        android:minSdkVersion="18"
        android:targetSdkVersion="24" />

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="被测应用包名" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="android.test.runner" />
    </application>

</manifest>

怎么运行

相关配置都到位了只要运行test_iteration()方法即可。

相关截图

跨应用

智能输入

红点标记

一触即达

FAQ

Q: 跳转到其他应用回不去怎么办?

A:可能存在机型兼容问题,如果遇到该情况可以把该 View 加入忽略数组。

Q:遍历时出现 object not found 怎么办?

A:Object 文件是记录类似序列化的传入参数,记录在sdcard/AutoClick/Object/目录下,务必保证它的存在。

Q:迭代模式下,一会就退出了,并没有遍历?

A:请检查sdcard/AutoClick/Params.json是否存在,或者该文件没有数据?

Q:自动遍历启动不了是什么情况?

A:请根据错误日志检查是否配置文件缺少必备参数,或者签名不一致?

Q:程序中途终止了?

A:确保数据线是连接状态,遍历需要用到 adb

Q:Android 6.0 及以上版本时,卡在授权界面?

A:如果第三方厂商更改过底层代码,可能出现兼容性问题(如小米),此时需要在Permission.java类中增加相应的包名及授权资源 id,通过uiautomatorviewer查看授权界面信息。

Github

AutoClick

交流群

结语

  • 欢迎提供指导性意见,大家一同探讨学习
  • 由于水平有限,文中如有错误,请轻拍
  • 代码已经开源,如果觉得对你有帮助,可以 star
  • 文章下方有打赏按钮,你的支持是我最大的动力

打赏支持

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 152 条回复 时间 点赞

非常强,666666666666666666666

很实用!!6666666

黑妞开挂的节奏!

感谢分享

谢谢分享,慢慢细品中~

挂都不是这么开的。。。

不错,速度有多快呢?

—— 来自 TesterHome 官方 安卓客户端

学习一下

厉害,晚上引入项目试用一下

Heyniu #10 · 2017年02月13日 Author

#1 楼 @sycing
#2 楼 @qg__gq
#3 楼 @xuxu
我来了,赶紧拿去用,别浪费了。

Heyniu #11 · 2017年02月13日 Author

#7 楼 @erickyang 正常操作 app 的速度,大概 1.5-2s 点击一次,嫌不够快可以把休眠时间设置小一点。
你看上面那张跨应用的 gif 图就知道有多快的,秒点

#5 楼 @lose github 给个星啊😆

能不能避免退出页面后很难回到该页面的问题?比如点了退出登录

老司机开车了

Heyniu #15 · 2017年02月13日 Author

#13 楼 @heyyuyu 有判断的,如果不小心退出登录了 点击其他 view 去到了登录页面会执行一次登录操作,或者最保险的把退出登录按钮资源 ID 加入忽略的 View 列表。

Heyniu #16 · 2017年02月13日 Author

#14 楼 @kasi 老司机

录制 intent 然后回放。楼主想法很给力,star 下。
1.如果 activity 通过非 intent 方式 就没法遍历到了吧?
2.遍历覆盖率取决于录制的操作?

Heyniu #18 · 2017年02月13日 Author

#17 楼 @zhangzhao_lenovo 绝大部分都是通过 intent 启动的,好像那种插件化以代理的形式代理全部生命周期的就不是通过 intent,不是很了解哈。
文中有提到 activity 的关联度,当然录制也是有关系的。
来个全功能测试,然后全部界面就有了吧,嘿嘿

#11 楼 @heyniu 目前我基于 uiautomator 做的自动遍历也是 1.5s 左右的时间,还以为可以达到 monkey 的速度。

—— 来自 TesterHome 官方 安卓客户端

Heyniu #20 · 2017年02月13日 Author

#19 楼 @erickyang 可以达到 monkey 的速度,我手动休眠了 1.5s 而已,你看一下文中的图,跨应用那个 gif 图,够不够快

你好,请问一下 ,LAUNCHER_ACTIVITY、homeActivity... 这些是只需要配置成 Actvitiy 的名称么(如:MainActviity)?执行时发现报错:Caused by: java.lang.ClassNotFoundException:,配置的 LAUNCHER_ACTIVITY 未找到

Heyniu #22 · 2017年02月13日 Author

#21 楼 @Tonyzhangcanon 看被测应用的是什么就是什么了,比如有的是:com.xx.homeactivity 有的是 com.xx.MainActviity

#22 楼 @heyniu 嗯,我的意思其实是假如是 com.xxx.xxx.act.MainActivity,我是需要写全,还是只需要写"MainActivity",就可以😜 还在尝试中...

Q:Android 6.0 及以上版本时,卡在授权界面?

这个问题可以通过解析AndroidManifest文件中的permission声明,应用安装之后启动之前再使用 adb shell pm grant <package name> android.permission.* 指令解决。

当然,这样并不符合真实用户的使用场景。

Heyniu #25 · 2017年02月13日 Author

#23 楼 @Tonyzhangcanon 写全,需要完整的路径

Heyniu #26 · 2017年02月13日 Author

#24 楼 @xubin98246 谢谢你提供的参考,应该有人用得上

你们的开发应该在膜拜你吧😂😂😂,黑妞出品,给力

—— 来自 TesterHome 官方 安卓客户端

how new be....
xue xi zhong...

恒温 将本帖设为了精华贴 02月14日 08:00

不错

加精理由:设计优秀 工具实用强大 介绍详细

在你的 github 上加上社区的链接吧 帮社区宣传下 今年的第三届中国移动互联网测试大会 有个优秀开源项目颁奖 你这个项目已经进入提名了 好好搞起来

这个对于 webview 是否友好?另外如果客户端 session 容易超时怎么办呢

Heyniu #34 · 2017年02月14日 Author

#27 楼 @hu_qingen 这倒没有,我请教过他们很多问题,应该感激他们才对

Heyniu #36 · 2017年02月14日 Author

#32 楼 @seveniruby 好,谢谢

Heyniu #37 · 2017年02月14日 Author

#33 楼 @Lihuazhang webview 没有过多的尝试,只是试过百度和自家产品的 web 页面,session 过时问题可以重新执行一次登录操作?或者由客户端本身控制吧

不错的实用工具

匿名 #39 · 2017年02月14日

这个要装 robotium、fiddler 么?可以跨平台么?像 fiddler 不支持 mac

Heyniu #40 · 2017年02月14日 Author

#39 楼 @Tank007 你装 Charles 也行啊,配合接口的不是必要的,只是方便查询错误

匿名 #41 · 2017年02月14日

#40 楼 @heyniu robotium 还需要装么?我看有很多 solo 调用

Heyniu #42 · 2017年02月14日 Author

#41 楼 @Tank007 不用,我在源码基础上改的

匿名 #43 · 2017年02月14日

#42 楼 @heyniu 不错,那要重新签名不?用的 debug 包

Heyniu #44 · 2017年02月14日 Author

#43 楼 @Tank007 重签名测试应用 就是 robotium 应用 签名与被测应用(你公司产品)一致,没有签名就重签名被测应用吧(不推荐)

Error:ProGuard: [AutoClick] java.io.IOException: Can't read /temp/proguard_input.jar
版本不兼容咋个弄- -

匿名 #46 · 2017年02月14日

#44 楼 @heyniu robotium 应用 签名与被测应用(你公司产品)一致,如何知道他们是否一致?

Heyniu #47 · 2017年02月14日 Author

#45 楼 @nil 有具体的信息吗

Heyniu #48 · 2017年02月14日 Author

#46 楼 @Tank007 如果不一致,启动时日志就会有报错输出

#47 楼 @heyniu 有的我截图吧,发现评论有显示限制

Heyniu #50 · 2017年02月14日 Author

#49 楼 @nil JDK 升级 1.8 吧

#50 楼 @heyniu 我们项目主要用的 1.7..不能升级 怕兼容出问题 我晚上回去再看一下 GitHub 已经加✨

Heyniu #52 · 2017年02月14日 Author

#51 楼 @nil 那就 autoclick 项目改 1.7 编译 然后报错的地方 自己兼容一下 应该也是 ok 的

#52 楼 @heyniu 嗯 实践一下回复

非常不错,mark

怎么做到无惧遮挡的😂 😂 appium 与 robotium 的差距么?

#37 楼 @heyniu 想请教一下,
在 Android 端 6.0 以上,在安装过程中,会出现授权问题。
这块是怎么解决的?

谢谢。

Heyniu #57 · 2017年02月14日 Author

#55 楼 @dadeshuo 是基于安卓的辅助功能实现的,拿货不是通过坐标点击的,所以不在屏幕范围内的也可以操作,有点黑科技的味道。

Heyniu #58 · 2017年02月14日 Author

#56 楼 @junewang 这个通过跨应用解决的,具体看
https://testerhome.com/topics/7273

#57 楼 @heyniu 求详细。。自己写 appium 还要一点一点滑屏,求优雅的方式!

Heyniu #60 · 2017年02月14日 Author

#59 楼 @dadeshuo 这个爱莫能助,appium 应该不能做到

public void test_iteration() 之前不需要加@Test吗? 否则怎么 run 起来?

#58 楼 @heyniu 好的,谢了。

#58 楼 @heyniu 如果多个页面的 activity 都一样,这种情况下,用快速遍历的化,还能实现吗?

Heyniu #64 · 2017年02月14日 Author

#61 楼 @ansonwoo junit4 不用加,我加了 编译器提示我去掉😂

Heyniu #65 · 2017年02月14日 Author

#63 楼 @junewang 在爬虫模式中,如果 activity 遍历过的就不会再遍历了,快速遍历可以实现,不排重的

在 Gradle 环境中又修改了几个 Error 之后已经可以运行
遇到的问题
1.首次启动时需要滑动的 Guide 页似乎没有操作,手动初始化之后启动了 loginActivityy
2.REPTILE 模式,登录成功后在首页一只没有操作,Log 有在打印(D/StrictMode:,好像似乎想先遍历 loginActivity 中的控件?一直在打印 restart activity : loginActivity)

可以提供一些 Log 中的 Tag 和关键字 ,方便确定下在做什么操作,遇到什么问题之类的😜

#64 楼 @heyniu 是 run as Android Junit Test 吧, 但找不到我电脑连着的 android devices. 是 api level 的问题?

Heyniu #68 · 2017年02月14日 Author

#66 楼 @Tonyzhangcanon 首页的滑动不会滑,直接跳过 来到登录页面,restart activity 是因为这个 activity 启动不了,你们的登录页面启动是否需要参数?因为我默认是没用参数启动的。日志过滤用 Robotium 可以看到所有操作日志

Heyniu #69 · 2017年02月14日 Author

#67 楼 @ansonwoo 这个是你环境问题了,是运行 Android Junit Test 没错

#68 楼 @heyniu 嗯,反正我们的 UI 自动化也是基于 Robotium 的,直接先执行初始化 Case 再遍历就好了;
我们的 LoginActivity 再登录之后再尝试启动会自动销毁的,是启动不了的,所以一直反复的 restart activity 。
想要用,可能还要在你的这个基础上再修改一下😃 ,还在研究中...

Heyniu #71 · 2017年02月14日 Author

#70 楼 @Tonyzhangcanon 你去除掉每次启动自动登录就好了

Heyniu #72 · 2017年02月14日 Author

#70 楼 @Tonyzhangcanon 加我企鹅 335827476,请教你点问题

😂 我想问下,像微信这种下面 4 个切换页面的,元素只不过是隐藏了么?辅助功能能直接无视这种结构去点击么??

Heyniu #74 · 2017年02月15日 Author

#73 楼 @dadeshuo 这个不能,因为页面还没加载出来啊,就是页面没初始化,要加载出来的,隐藏了就可以点

厉害。留着慢慢学习!

(1) bin 下面一直会生成 AutoClick.apk, 然后安装这个 apk,我指定的 apk 怎么没安装啊?
(2) 我在 Project 竟然搜不到"Uploading AutoClick-master.apk onto device..." 这句话. 打开方式不对?

Heyniu #77 · 2017年02月15日 Author

#76 楼 @ansonwoo 自己装哇,没有关联你的被测应用的,还有看你提示测试 app 被测 app 签名不一致哦

#77 楼 @heyniu 不是很明白 "because package *.test does not have a signature matching the target *". 你这不是重新安装, 也不是同一个渠道包升级, 为什么要求签名一致呢?

Heyniu #79 · 2017年02月15日 Author

#78 楼 @ansonwoo 任何情况下 测试 app 与被测 app 签名需保持一致

匿名 #80 · 2017年02月16日

#79 楼 @heyniu
一直提示 “Error:Android Packager: [AutoClick] Cannot create new key or keystore”
AndroidManifest.xml 里面修改了获取到的包名,没有重新签名,已经懵逼了

Heyniu #81 · 2017年02月17日 Author

#80 楼 @Tank007 哥们,不是叫你换签名吗,你红框的是我项目的签名,换成你项目的签名

很好很强大,感觉好东西都是 java 写的。

传送门是 home 的地址,赞一个。

我看了下,挺不多,很符合。但我不是很懂,我先赞下,再来琢磨,学习,运用到项目上。感谢。


@heyniu 请问这是什么原因

Heyniu #86 · 2017年02月23日 Author


这个 test runner 要重新用编译器改成 com.heyniu.auto.InstxxxxRunner

😪 小白还不太会用,先收藏,再学习

Heyniu #88 · 2017年02月24日 Author
guard 回复

放着,养肥了再说

@heyniu 改成自己的 keystore 后,报错如下 ,是需要哪里设置密码吗?
Error:Android Packager: [AutoClick] java.io.IOException: Keystore was tampered with, or password was incorrect

Heyniu #90 · 2017年02月24日 Author
jennyyung 回复

这个你问下你们的研发吧

@heyniu ,有些应用不需要登录的,有跳过的配置?

Heyniu #92 · 2017年02月25日 Author
lsy 回复

直接填空格就好,不能是 null or ""

AutoClick 是单独的一个 module 吗? 配置了 LAUNCHER_ACTIVITY 是被测 app 的 PackageName+ActivityName 还是提示 java.lang.ClassNotFoundException。应该怎么配置?

Heyniu #94 · 2017年02月27日 Author
隐身 回复

应该是编译器设置的 test runner 与实际的不符

暂时放弃了,以后有机会再细细的读一遍,赞一个!~

Heyniu #96 · 2017年02月28日 Author
magicyang 回复

好多都卡环境上了,这是我没预算到的

一切技术都为解决实际问题服务,我更感兴趣的是关于多重跟踪这块,希望有更多的图文描述,以供借鉴。

我之前就说了哪个测试工具或者框架能解决环境搭建问题,基本就成功了 80%

Heyniu #99 · 2017年03月06日 Author
扫地僧 回复

举个栗子

当发生崩溃时

  • sdcard/AutoClick/package/Crash目录下会产生一个Crash日志

  • 假设日志信息如下

  • 一般日志中有提到哪个界面出现崩溃

    • 如果日志被混淆则,对照mapping.txt查找
  • 如果没有提到哪里出现崩溃则在sdcard/AutoClick/package目录下打开Activities.txt查看最后打开的界面是哪个

  • 通过以上步骤能拿到最后的一个遍历的界面,再到sdcard/AutoClick/package/Screenshots目录下查找该界面的文件夹进入,方可看到遍历该界面的步骤截图

排查

  • 如果截图信息不够详细,可以打开sdcard/AutoClick/package/Log目录下最新的 log 文件,里面记录着以下日志
    • 被测 app 打印的日志(如果有)
    • 自动遍历操作日志
    • 点击的元素日志
  • 一般根据以上信息足以跟踪Crash来源
  • 接口的配合(如果遍历时有开启代理)
    • Fiddler 代理抓取
    • Charles 代理抓取
  • 根据Crash发生的时间逐一排查对应的截图、日志、接口信息尝试手动操作复现

进阶

  • 复现Crash后打开对应的源码,尝试修复
    • 一般通过接口、崩溃日志可定位是接口问题,还是客户端问题
  • 客户端问题
    • 如果是比较简单的错误,如空指针、数组越界等,在发生问题行修改即可(一般情况)
    • 如果是复杂问题,则多打断点调试,找出元凶,根除问题(一般是由于其他问题引起此处代码背锅)
    • Fix bug
    • 编译重测
  • 下一步
    • 联系研发修复 bug(此时自己的修复不提交,不符流程,只是作为练手)
    • 研发 fix 后,查看研发的修复跟自己的是否一致
    • 如果一致,那么恭喜你,你可能已经掌握了这一块的逻辑或者是业务
    • 如果不一致,那么可以对比下,以待进一步提高
    • 久而久之,业务逻辑、研发的处理逻辑、套路,你都会更加了如指掌
    • 良性循环,对业务、源码都有更大的把握,也能找到更深层次的问题,从而推进质量的提升
Heyniu #100 · 2017年03月06日 Author
恒温 回复

看来,有时间我要写一个非常详细的文档才行

Heyniu 回复

人工查有点繁琐,能不能把线索串起来更直观的展现给用户呢

Heyniu #102 · 2017年03月06日 Author
扫地僧 回复

原理都说了,怎么串起来还有难度?

Heyniu 回复

我做的那个就串起来了

Heyniu 回复

是的,因为太多人都没多少基础,但是又需要短时间内用你这个工具,比如说我。我看过思寒的 AppCrawler,有视频和文档指引,就很容易上手,但那工具略慢....😂

楼主,这玩意怎么用,有 demo 介绍吗,实在看不懂,但是又想用。。。

弄了好些时间还是跑不起来,测试启动后被测应用界面闪了下后报错:Test run failed: Instrumentation run failed due to 'java.lang.NullPointerException'
logcat 的日志是:
E/AndroidRuntime( 7643): java.lang.RuntimeException: Unable to start activity ComponentInfo{PACKAGENAME/PACKAGENAME.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.TaskStackBuilder android.support.v4.app.TaskStackBuilder.addParentStack(android.app.Activity)' on a null object reference
E/AndroidRuntime( 7643): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
E/AndroidRuntime( 7643): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
E/AndroidRuntime( 7643): at android.app.ActivityThread.access$800(ActivityThread.java:144)
E/AndroidRuntime( 7643): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
E/AndroidRuntime( 7643): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime( 7643): at android.os.Looper.loop(Looper.java:135)
E/AndroidRuntime( 7643): at android.app.ActivityThread.main(ActivityThread.java:5221)
E/AndroidRuntime( 7643): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 7643): at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime( 7643): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
E/AndroidRuntime( 7643): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
E/AndroidRuntime( 7643): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.TaskStackBuilder android.support.v4.app.TaskStackBuilder.addParentStack(android.app.Activity)' on a null object reference
E/AndroidRuntime( 7643): at android.support.v7.app.AppCompatActivity.onCreateSupportNavigateUpTaskStack(AppCompatActivity.java:353)
E/AndroidRuntime( 7643): at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:74)
E/AndroidRuntime( 7643): at PACKAGENAME.MainActivity.onCreate(MainActivity.java:64)
E/AndroidRuntime( 7643): at android.app.Activity.performCreate(Activity.java:5933)
E/AndroidRuntime( 7643): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
E/AndroidRuntime( 7643): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
E/AndroidRuntime( 7643): ... 10 more
W/ActivityManager( 562): Error in app PACKAGENAME running instrumentation ComponentInfo{PACKAGENAME.test/com.heyniu.auto.InstrumentationTestRunner}:
W/ActivityManager( 562): java.lang.NullPointerException
W/ActivityManager( 562): java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.TaskStackBuilder android.support.v4.app.TaskStackBuilder.addParentStack(android.app.Activity)' on a null object reference

请问是哪里出问题了?对 robotium 不熟悉..

Heyniu #107 · 2017年03月17日 Author
jb 回复

demo 后续有空先吧,自己先折腾

Heyniu #108 · 2017年03月17日 Author
Samu Chao 回复

V4 包没有这个方法,你看下升级下 V4 包,问下你们技术 V4 包版本多少的 替换掉我的,我的是 23.3.0

下面是我执行前的环境配置,给需要的同学个参考:
1\使用 eclipse 导入工程:imports as android project;
2\更改 project build target 为 7.1.1;
3\更改 jdk complier compliance level 为 1.7(本地的 java -version 为 1.8);
4\处理些报错提示,主要是添加 final;
5\manifest 里面替换被测应用包名;
6\Iteration.java 文件中,填写 LAUNCHER_ACTIVITY 和 homeActivity,登录相关的因为用不到用" "代替,其他用不到部分清空;
7\因为项目没有登录页面,屏蔽 test_00_login 的代码;
8\被测项目用的 v4 版本更新,更新 support-v4-23.3.0(删除自带的 support-v4 后通过 android tools 的 add support library 更新);
9\安装后使用命令执行测试:adb shell am instrument -w -r -w -e debug false -e class application.iteration.Iteration#test_01_iteration 被测应用包名.test/com.heyniu.auto.InstrumentationTestRunner
使用 eclipse 直接执行会报 Process crashed

@heyniu 拉了代码实验了下,对部分控件可能支持不是很好,例如 spinner,不过思路还是不错

Heyniu #112 · 2017年03月28日 Author
jc 回复

😆 正常啦

主要是来学习老司机的思路。

Heyniu 回复

今天想用下你的自动遍历方案,导入工程后,很多文件错误,想问下这是什么原因导致的?首先我认为你的源码应该是没问题的,但却有很多红叉,是因为我缺少什么吗?比如

Heyniu #115 · 2017年03月28日 Author
周小丽 回复

你的 JDK 版本换成 1.8 的来编译就没这些问题

Heyniu 回复

我 JDK 是 1.84 的

Heyniu #117 · 2017年03月28日 Author
周小丽 回复

你要把运行时的 jdk 调成 1.8,估计你现在的是 1.6 或 1.7,问下你开发

Heyniu 回复

你说的应该是我本地测试环境下的 JDK 吧?这跟开发没关系啊

Heyniu #119 · 2017年03月29日 Author
周小丽 回复

算了,我打开 eclipse 告诉你吧

Heyniu #120 · 2017年03月29日 Author
周小丽 回复

Heyniu 回复

按你这设置后还真少了很多红叉,但还有些红叉,如下提示不知怎么处理,麻烦了啊
[2017-03-29 09:56:45 - AutoClick-power] Android requires compiler compliance level 5.0 or 6.0. Found '1.8' instead. Please use Android Tools > Fix Project Properties.

Heyniu 回复

我换成 1.7 没那个提示了,但红叉文件仍存在

Heyniu #123 · 2017年03月29日 Author
周小丽 回复

你安卓 SDK 版本没这么高,你把 lolipop 的都改成 21

我现在没有红叉文件了,但提示 Android requires compiler compliance level 5.0 or 6.0. Found '1.8' instead. Please use Android Tools > Fix Project Properties.,,,请问你说的 “把 lolipop 的都改成 21”,我看了半天没找到位置

Heyniu 回复

好想用起来 看看是什么效果,是不是只需要配置如下 2 张图即可?配置对不

Heyniu #126 · 2017年03月29日 Author
周小丽 回复

这里不用全路径,直接:bt_xx 其他应该没错

Heyniu 回复

妞,我现在没有红叉文件了,但提示 Android requires compiler compliance level 5.0 or 6.0. Found '1.8' instead. Please use Android Tools > Fix Project Properties.,,,请问你说的 “把 lolipop 的都改成 21”,我看了半天没找到位置

Heyniu #128 · 2017年03月29日 Author
周小丽 回复

我觉得这些环境问题你可以找你们开发帮忙的

恒温 [LuckyFrame] V1.0 版本-开源自动化平台 中提及了此贴 03月29日 15:17
130楼 已删除
Heyniu 回复

请问下,签名文件放在哪个目录下啊?运行报错:Test run failed: Permission Denial: starting instrumentation ComponentInfo{com.qshealthcare.com.wtmd.test/com.heyniu.auto.InstrumentationTestRunner} from pid=16063, uid=16063 not allowed because package com.qshealthcare.com.wtmd.test does not have a signature matching the target com.qshealthcare.com.wtmd

Heyniu 回复

嗨 妞,我没找到签名文件放的目录,麻烦帮看下

@heyniu 用 szhomebbs.keystore 重签名我的应用,需要用户名和密码么?

Anson 回复

问下你最后那个签名的问题是怎么解决的啊?我不知道签名文件放哪

Samu Chao 回复

@samu 8\被测项目用的 v4 版本更新,更新 support-v4-23.3.0(删除自带的 support-v4 后通过 android tools 的 add support library 更新);
替换后不影响编译吧?

dadeshuo 回复

授权问题,appium 可以解决啊,同时也可以不用滑屏吧 获取 android 常见控件名称,开启多线程,不停获取可以点击的控件,然后点击就可以了吧

弄了两天了,一直运行不成功,楼主能写个环境搭建的流程吗,毕竟每个人环境不一样,都快放弃 了

Heyniu #138 · 2017年04月17日 Author
邓琳 回复

如果环境问题搞不定的话,那你暂时还是放弃吧

你好,我这边弄下来提示找不到依赖了,是什么更新了吗请问,需要如何解决

junit.framework.AssertionFailedError: Exception in constructor: test_00_login (java.lang.ClassNotFoundException: com.qianxun.comic.apps.WelcomeActivity
环境都配置好了,报这个错,我是 android studio 上面弄的,请问大神,我这边是上面步骤没弄好吗

Heyniu #142 · 2017年04月18日 Author
深蓝 回复

robotium 的包别用官方的,用我项目中的 5.6.3

Heyniu 回复

有什么不一样啊,做了什么改动了

Heyniu #144 · 2017年04月19日 Author
bauul 回复

把一些 private 改成了 public

Heyniu 回复

库是用的你的库

深蓝 回复

打开 Run/Debug Configurations,看看是否已 android junit 启动了?
改成以 Android Instrumented tests 方式启动就好了
我也是用的 android studio

http://stackoverflow.com/questions/2422378/intellij-idea-with-junit-4-7-junit-version-3-8-or-later-expected
The latest Android Studio might have this issue with slightly different case, the test class/method should be fall under Android Tests instead of JUnit in Run/Debug Configurations. See my answer below.

Anson 回复

@ansonwoo 求问这个问题是如何解决的?

@heyniu 想请问一下,对于已经安装在系统里的应用是否可以遍历?无 APK。

深蓝 回复

请问这个问题解决了吗,是怎么解决的呢

@heyniu 楼主好,我把你的遍历工程导入到 Android Studio 上。在 Android Studio 可以运行,但是想通过断点的方式看看整体的执行流程,却发现不能 debug。网上找了好久也没解决,请问需要什么配置么。

错误告警如下:

Error running Iteration:
Cannot debug application from module AutoClick on device lge-nexus_5-055846c6f0df5ffb.
This application does not have the debuggable attribute enabled in its manifest.
If you have manually set it in the manifest, then remove it and let the IDE automatically assign it.
If you are using Gradle, make sure that your current variant is debuggable.

Heyniu #151 · 2017年08月03日 Author
onesbyones 回复

不用 debug,你打印日志或者看源码就行了

Crazyerick 回复

基于 uia 的开源么

Heyniu 回复

请问,你这个工程,我可以导入到 android studio 中么?非常感谢!

Heyyu 回复

楼主,能不能提供一个 andorid studio 工程啊,现在很少人用 eclipse 了。

@heyniu ,为什么启动 app 时会出现 java.lang.IncompatibleClassChangeError,好像是 support 包冲突,只使用 robotium 可以启动 app,一旦添加了 support 包,就会出现这个错误,该怎么解决啊?

需要 登录 後方可回應,如果你還沒有帳號按這裡 注册