
## 1. 专项测试
* 业务测试:面向新需求
* 回归测试:面向已交付需求
* 专项测试:面向非功能需求的各类质量唯独特征
表现 | 用户维度 | 技术维度 |
崩溃 | crash,弱网 | 检测崩溃1.某个页面,因为研发处理不合适,点击页面的某个控件、页面加载中接口报错(如返回null值,或空列表)、前端的列表计算有问题(数组越界)、内存问题引发崩溃,一般使用自动遍历、monkey测试频繁的去检测app的健壮性,模拟大量的随机事件。新业务中一般使用断点模拟接口异常,使用自动遍历、monkey测试不一定能有效模拟这种场景。2.还有一些特殊行为也会触发,比如横竖屏切换3.进入一个app,快进快退,也会引发崩溃。 |
卡顿 | 掉帧、gc、CPU | 卡顿测试:需要借助与安卓官方提供的技术手段,进行相关的数据分析。原因:CPU使用、GPU渲染,gc,内存泄漏内存泄漏测试method profile |
响应慢 | 启动时间、交互响应、H5加载 | 响应时间:分为冷启动与热启动交互响应:界面切换分析起接口调用时间和处理过程H5性能测试:严格分析出来资源的消耗情况 |
发热、掉电快 | CPU、MEM、IO、network、GPS等硬件使用 | method profile(了解CPU的使用情况)、gc统计、IO使用、流量消耗、硬件(屏幕等)使用统计、耗电量统计 |
兼容性问题 | 机型覆盖、回归 | 兼容性测试、自动化测试、自动遍历、monkey测试主要与机型有关,手机品牌日益聚焦,目前覆盖TOP100就够了。 |
2. 常用的解决方案:
- ddms
* 谷歌开发的安卓分析工具套件,不过逐渐废弃了。 - Android studio最新版本的集成工具
* 关于性能的分析,正在逐渐有独立的工具编程IDE自身的集成。 - 代码插桩
* 可以帮我们发现很多的问题,比如内存的泄漏,对CPU的使用,当前CPU与内存的占用情况,在代码层也可以分析出来。在应用中嵌入一些代码统计指标也可以分析。有些大厂自带了性能监控的SDK,不断统计手机的网络消耗、CPU与内存消耗,上传到服务端进行综合的分析,判断用户有没有出现崩溃、内存不足等手机各方面的数据。
3. 崩溃问题检测
3.1 典型问题:
ANR
假设主线程发起一次网络请求,这次网络请求5秒内没有得到响应,被系统检测到,系统就会弹窗提示ANR。ANR在6.0之前非常多,现在很少了。原因是在开发app的时候,如果在原声页面发起一次网络请求,对于Android Studio来说在编译时就编译不过,会告知这块代码有风险,必须要改,否则编译不过。Force Close
假设页面有一个列表,在处理列表是,出现数组越界,系统弹窗提示Force Close原生的Crash
用C语言或其他语言在底层封装了一些库,结果这个库崩了,而异常未捕捉,也会崩溃
3.2 基本测试方法
- 发布前
* monkey测试+AppCrawler自动遍历:遍历所有的界面,创造一些随机时间,来去模拟能不能触发系统各种诡异的bug
* 结合各类场景用例 - 发布后
* 埋点 在主线程,注册一个异常处理逻辑就可以了
* 接入外部SDK buglly本质在app内注册一个线程的异常处理机制,一旦线程崩溃,他会首先捕获这个异常,把这个异常统计分析,发送到云端
3.3 常见场景
3.3.1 接口返回异常(一般通过Charles或mock来实现)
- 弱网
* 完全超时
* 2G 3G
* 场景:设定一个弱网的情况如2G,或者让响应时间超过3秒钟。进入app,立即退出(可以通过自动化去模拟),此时观察系统有没有出现问题。进入app的时候,应用会发起一个网络请求到服务器。用户发现页面反应比较慢,返回,此时发起网络请求的异步线程还在请求,退出时并未取消请求。请求完成后,就回去渲染当时的页面,如果开发的代码有问题,在异步线程里去渲染一个已经退出的页面即空对象,就会出现空指针。 - null返回:后台的某些接口出现Null或空列表的返回,应用也要能够处理
- 字段类型变更:某些开发随意修改返回值类型,造成客户端崩溃。
3.3.2 逻辑问题:
- 打开新页面再快速返回,异步线程问题
- 横竖屏切换、前后台切换
3.4 崩溃分析
- 1.日志,过滤Exception字段
- 2.traces.txt文件分析,参见https://www.jianshu.com/p/ac3a7c28b830
4. 交互体验
4.1 原生页面响应时间
- app交互
* 冷启动、热启动
* 事件响应、内部加载速度 - 接口性能
- h5加载
4.1.1 冷启动—adb
- 传统的方式,基本已废弃 没有其他相关技术,可以使用传统方式简单统计各个Activity的响应时间,但其时间是不准的。
- 埋点 更多时候是采用埋点,一般一个app会在应用的各个界面进行埋点。比如Activity加载需要多久,这中间又有好几个处理过程,比如去访问服务端获取数据,客户端在获取到数据后再进行渲染。在不同的阶段里面创建自己的埋点,借助埋点去统计各个过程的耗时,这种统计才是准确的。
下面2种统计方式都只统计了Activity的展现时间,里面的数据并未完全加载完。如果想统计数据完全的被加载,还需要另外的统计方法。虽然这2种方法不是特别准确,还是能发现不少问题。
adb shell am force-stop com.UCMobile
adb shell am start -S -W com.UCMobile/com.uc.browser.InnerUCMobile
#ThisTime:最后一个启动的Activity的启动耗时;
#TotalTime:自己的所有Activity的启动耗时;
#WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)
参考:https://blog.csdn.net/yan_startwith2015/article/details/77991571
```#shell
adb shell am force-stop com.UCMobile
adb logcat |grep ActivityManager.*Displayed
```
4.1.2 冷启动—埋点
为了更准确统计启动时间,一般采用埋点。app会在各个界面进行埋点,Activity加载需要多久,中间还有好几个处理过程,比如访问后端去请求数据再进行渲染,根据埋点统计各个过程。
埋点有2种方式:研发团队埋点、通用的SDK埋点
应用启动(Application onCreate)后,进入主线程(Main Thread),在主线程中初始化Activity(Activity init),创建Activity(Activity onCreate),之后加载事件、渲染布局、展示,此时页面能够展示。除此之外还有异步加载。DisplayedTime和reportFullyDrawn是比较关注的。相比于adb统计,埋点统计更精准。
主要的流程:
- Application OnCreate
* 加载第三方的sdk - ActivityOnCreate
* 加载自身的逻辑
* 发送请求获取数据 json
* 渲染界面 List
使用第三方sdk:
<!--1.集成SDK,在Module的build.gradle文件中添加依赖和属性配置-->
dependencies {
compile 'com.tencent.bugly:crashreport:latest.release' //其中latest.release指代最新Bugly SDK版本号,也可以指定明确的版本号,例如2.2.0
}
<!-- 2.初始化,获取APP ID并将以下代码复制到项目Application类onCreate()中,Bugly会为自动检测环境并完成配置-->
CrashReport.initCrashReport(getApplicationContext(), "注册时申请的APPID", false);
有很多的sdk和系统的默认设置都会加到Application类或Activity类的onCreate()中,如第三方消息推送,这就导致onCreate()的逻辑较多,进而影响应用的启动、界面的加载都会有不同程度的影响,因此启动事件需要使用合适的方法进行度量。
4.1.3 代理工具:Activity启动事件+接口响应时间(第一个接口的发起时间和最后一个接口的响应完成时间)
- 1.使用adb查看Activity的启动事件
- 2.借助于Charles等抓包工具可以获取接口耗时 使用代理工具抓包,分析界面到底有多少个网络请求,每个网络请求的耗时
4.2 H5页面
可以使用chrome://inspect查看设备上H5页面的响应。
chrome部分版本取消了一个css标签的支持,导致页面混乱,此时可以下载版本62的chrome
1.根据版本号获取id https://omahaproxy.appspot.com/ 如62.0.3202.62的id为499098
2.替换id去下载chrome https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Mac/499098/
4.2.1 以浏览器模拟手机,页面如下
每个字段具体意义https://developers.google.com/devtools/docs/network#resource-network-timing
Request Table默认显示以下类目:
- Name 资源的名称
- Status HTTP状态码
- Type 已请求资源的MIME类型
-
Initiator 发起请求的对象或进程。值为以下选项之一
* Parser Chrome的HTML解析器发起请求
* Redirect HTTP重定向发起请求
* Script 脚本发起请求
* Other 某些其他进程或操作发起请求,例如用户通过连接或者在地址栏输入网址导航到页面 - Size 响应标头(通常为数百字节)加响应正文(有服务器提供)的组合大小
- Time 从请求开始至响应中接收到最终字节的总持续时间
- Timeline 可以显示所有网络请求的可视瀑布。点击此列的标题可以显示一个包含更多字段的菜单。
4.2.2查看DOMContentLoaded和load事件信息
Network面板突出显示两种事件:DOMContentLoaded和load。
解析页面的初始标记时会触发DOMContentLoaded。此事件将在Network面板的两个地方显示
- Overview窗格中的蓝色竖线表示事件
- 在Summary窗格中,可以看到时间的确切时间
页面完全加载时将触发load。此事件显示在三个地方
1. Overview窗格中的红色竖线
2. Requests Table中的红色竖线也表示事件
3. 在Summary窗格中,可以看到事件的确切时间
ResourceTiming API提供了接受各个资源事件的有关的大量详细信息。
4.2.3 请求生命周期的主要阶段包括:
- 重定向
* 立即开始 startTime
* 如果正在发生重定向,redirectStart也会开始
* 如果重定向在本阶段未发生,将采集redirectEnd - 应用缓存
* 如果应用缓存在实现请求,将采集fetchStart时间 - DNS
- TCP
* connectStart 在初始连接到服务器时采集
* 如果正在使用TLS或SSL,secureConnectionStart将在握手(确保连接安全)开始时开始
* connectEnd将在服务器的连接完成时采集 - 请求
* requestStart 会在某个资源的请求被发送到服务器后立即采集 - 响应
* responseStart 是服务器初始响应请求的时间
* responseEnd 是请求结束并且数据完成检索的时间
如果有重定向,就进行重定向;如果没有重定向,就判断本地有没有cache。如果资源没有访问过,就查DNS,找网站。找到网站,与具体的网站建立连接,连接完成后,发起请求,服务端进行响应。此时界面仍然是空白的,直到DOMContentLoaded事件,load事件表明页面加载完成
4.2.4 Devtools中查看指定条目完整的耗时信息
-
Queuing 如果某个请求正在排队,则指示:
* 请求已被选人引擎推迟,因为该请求的优先级被视为低于关键资源(例如脚本/样式)的优先级。图像经常大声这种情况。
* 请求已被暂停,以等待将要释放的不可用TCP套接字
* 请求已被暂停,因为在HTTP 1.0上,浏览器仅允许每个源拥有6个TCP连接
* 生成磁盘缓存条目所用的时间(通常非常迅速) - Stalled/Blocking 请求等待发送所用的时间。可以是等待Queuing中介绍的任何一个原因。此外,此时间包含代理协商所用的任何时间
- Proxy Negotiation 与代理服务器连接协商所用的时间。
- DNS Lookup 执行DNS查询所用的时间。页面上的每一个新域都需要完整的往返才能进行DNS查询。
- Initial Connection/Connecting 建立连接所用的时间,包括TCP握手/重试和协商SSL的时间
- SSL 完成SSL握手所用的时间
- Request Sent/Sending 发出网络请求所用的时间。通常不到1毫秒
- Waiting(TTFB 等待初始响应所用的时间,也称为第一字节的时间。此时间将捕捉到服务器往返的延迟时间,以及等待服务器传送响应所用的时间。谷歌建议至少在200毫秒以下
- Content Download/Downloading 接收响应数据所用的时间
4.2.5 webview开关
模拟器默认支持
针剂需要打开app内开关
//启用WebView调试,需要在WebView类上调用静态方法setWebContentsDebuggingEnabled
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
WebView.setWebContentsDebuggingEnabled(true);
}