基于代码覆盖率的精准测试提效实践

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. 基于代码覆盖率的精准测试提效实践 有赞零售 秦阳
2. ⽬目录 • 技术背景 • 检测原理理 • 系统架构 • 案例例分析
3. 技术背景
4. ⼿手⼯工测试⽆无法量量化,依赖业务经验 • • • • • • 这些测试⽤用例例够不不够? 这些代码执⾏行行了了没有? 这⾥里里的逻辑遗漏漏了了? 1000 ⾏行行代码 开发 20 个测试⽤用例例 这些⽤用例例是否覆盖所有场景? 这个项⽬目的需求都测到了了吗? 上次新增代码都测到了了吗? 20 个真实场 景测试⽤用例例 测试
5. 单测的维护成本⾼高 模块 总代码 已执⾏行行 覆盖率 ⾸首⻚页 300 300 100% 购物⻋车 300 240 80% 设置 200 0 0% …… …… …… …… 5000 ⾏行行代码 10 个单元测试⽤用例例
6. ⼿手⼯工测试+代码覆盖率 同⼀一个版本的App安装到多个设备进⾏行行测试 不不同版本的App在多个设备中进⾏行行测试 代码合并到发布分⽀支后,⽣生成回归包进⾏行行回归 …… …… 功能包A …… ? 模块 总代码 已执⾏行行 覆盖率 ⾸首⻚页 300 ? ? 购物⻋车 300 ? ? 设置 200 ? ? …… …… …… …… 功能包N …… 回归包
7. 检测原理理
8. 三种插桩⽅方式 - 源码插桩 - 中间⽂文件插桩 - 可执⾏行行⽂文件插桩 - iOS(OC) LLVM 针对IR插桩 Android Jacoco offline插桩技 术,通过ASM修改字节 码插桩
9. iOS编译插桩流程 源代码⽂文件 预处理理程序 预处理理⽂文件 编译插桩(iOS) 汇编⽂文件 汇编程序 ⽬目标⽂文件 链接程序 可执⾏行行⽂文件
10. 基本块(Basic Block) 只有⼀一个⼊入⼝口 只有⼀一个出⼝口 只要基本块中第⼀一条指令被执⾏行行,基本块内所有指令都会执⾏行行 - 双层扫描: 遍历所有函数 遍历函数内的基本快 BB1 BB2 BB3
11. 函数中基本块的插桩⽅方法 代码⽚片段 开始 执⾏行行 A if (需要执⾏行行B) 执⾏行行 B else 执⾏行行 C 结束
12. 函数中基本块的计数⽅方法 代码⽚片段 初始化计数器器 count[] 开始 计数器器⾃自增 count[0]++ 执⾏行行 A if (需要执⾏行行B) 计数器器⾃自增 count[1]++ 执⾏行行 B else 计数器器⾃自增 count[2]++ 执⾏行行 C 计数器器⾃自增 count[3]++ 结束 guessNumber=33 1 1 0 0 0 1 1
13. iOS编译插桩环境配置 1 Xcode build 编译配置,启动编译插桩,⽣生成gcno插桩记录⽂文件 (记录着插桩位置)
14. iOS编译插桩环境配置 2 通过Build Phases脚本,在编译成功后上传gcno⽂文件 gcno⽂文件格式 ⽂文件结构 源码 函数结构 BB 结构 BB ⾏行行结构 插桩代码
15. iOS编译插桩环境配置 3 保存gdca数据到⽂文件并上传(gcda由程序执⾏行行后⽣生成) 代码⽚片段 初始化计数 开始 计数器器⾃自增 执⾏行行 A if (需要执⾏行行B) 计数器器⾃自增 执⾏行行 B gcda⽂文件格式 基本块标识 计数器器信息 count[0]的值 … else 计数器器⾃自增 执⾏行行 C 计数器器⾃自增 结束 count[1]的值
16. gcda和gcno⽂文件⼀一⼀一对应
17. 系统架构
18. 代码覆盖率平台架构 模块 数据可视化 数据解析 基础解析器器 增量量解析器器 合并解析器器 总代码 已执⾏行行 覆盖率 ⾸首⻚页 300 300 100% 购物⻋车 300 240 80% 设置 200 0 0% …… …… …… …… 代码执⾏行行信息上传 数据采集 覆盖率采集 源码采集 APP构建 APP 分发 Hook脚本 代码插桩 CI App安装 App功能测试
19. 有赞App构建分发平台 触发构建 提 交 代 码 开发 构建成功 编译插桩(iOS) 字节码插桩(Andorid) 插桩⽂文件和源码打包上传 分 发 下 载
20. 覆盖率信息收集 APP构建系统 插桩记录 源码 分 发 下 载 App信息 设备类型 执⾏行行记录 场景: 1 同样的App包,⼀一个设备上可能多次 上传覆盖率信息 2 同样的App包安装在不不同的设备上, 各⾃自上传覆盖率信息 3 AppID = bundle+branch+commit
21. 覆盖率解析 基本覆盖率 整体覆盖率 周期覆盖率
22. 基本覆盖率 应⽤用场景:同样的App包,单个设备会单次上传覆盖率信息 数据采集 函数名 + 执⾏行行次数 基本块标识 + 执⾏行行次数 ⽂文件路路径 函数名 + ⾏行行号 代码插桩信息 代码执⾏行行记录 校验信息 基本块结构 + ⾏行行信息 覆盖率信息解析 iOS: LCOV Android: Jacoco 单次覆盖率数据 ⽂文件路路径 函数名 + ⾏行行号 函数名 + 函数执⾏行行次数 代码⾏行行执⾏行行次数 + ⾏行行号
23. 整体覆盖率 数据采集 应⽤用场景: 同⼀一个App包,单个设备会多次上传覆盖 率信息 同样的App包安装在不不同的设备上,分别 上传覆盖率信息 代码插桩信息 代码执⾏行行记录 覆盖率信息解析 设备 A 覆盖率数据 …… 设备 N 覆盖率数据 整体覆盖率 = 单个App包多设备的覆盖率总和 多设备同⼀一个App整体覆盖率数据
24. 周期覆盖率 应⽤用场景: ⼀一个开发分⽀支上会有多次提交,⽐比如bug修复 多个分⽀支上开发的功能,最后合并在⼀一起发版 周期覆盖率 = ⼀一定周期内App所有包的覆盖率总和
25. 周期覆盖率 版本 A 覆盖率数据 数据预处理理 解析覆盖率数据 解析增量量代码 ⾏行行号迁移 …… 版本 N 覆盖率数据 覆盖率数据预处理理 CovInfo ⾏行行号分析+迁移 程序源码 git diff diffFile 解析Diff diffInfo 版本增量量覆盖率数据 合并 多版本全量量覆盖率数
26. 解析两端覆盖率数据 CovInfoMap key:/coverageDemo/coverage/hello.c value:List[CovInfo] ⾏行行号:8 ⾏行行号:15 ⾏行行号:20 执⾏行行次数:1 执⾏行行次数:0 执⾏行行次数:1 函数名:main 函数名:fun1 函数名:fun2 …… ⾏行行号:11 ⾏行行号:12 ⾏行行号:17 执⾏行行次数:1 执⾏行行次数:1 执⾏行行次数:0 函数名:空 函数名:空 函数名:空 ……
27. 解析增量量代码 SourceCode #include<stdio.h> void fun1(); void fun2(); int main(int argc,char* argv[]) {     if(argc>1)         fun1();     else         fun2(); printf(“AAA\n"); printf("BBB\n");     return 0; }
28. 解析增量量代码 DiffFile 改动范围 删除代码 新增代码 diff --git a/coverage/hello.c b/coverage/ hello.c index bdfdbce..a48a0fb 100644 --- a/coverage/hello.c +++ b/coverage/hello.c @@ -8,6 +8,7 @@ int main(int argc,char* argv[]) If (argc>1) fun1(); else - fun2(); + printf(“AAA\n"); + printf("BBB\n"); return 0; }
29. 解析增量量代码 List<DiffInfo> ⽂文件路路径:/coverage/hello.c Diff 类型:修改⽂文件(增删改重) 历史路路径:空(重命名有) List<DiffBlockInfo>: 起始删除⾏行行号:11 删除⾏行行数:1 起始增加⾏行行号:11 增加⾏行行数:2
30. 覆盖率信息合并 覆盖率A 覆盖率N ?
31. ⾏行行号迁移 A-CovInfoMap ⾏行行号:10 执⾏行行次数:1 函数名:空 List<DiffInfo> ⽂文件路路径:/coverage/ hello.c Diff 类型:修改⽂文件 历史路路径:空 List<DiffBlockInfo>: ⾏行行号:12 起始删除⾏行行号:11 执⾏行行次数:1 删除⾏行行数:1 函数名:空 起始增加⾏行行号:11 ADiff-CovInfoMap ⾏行行号:10 执⾏行行次数:1 函数名:空 ⾏行行号:13 执⾏行行次数:1 函数名:空 增加⾏行行数:2 …… ……
32. ⾏行行号迁移 List<DiffInfo> A-CovInfoMap ⾏行行号:10 执⾏行行次数:1 函数名:空 ⾏行行号:12 ADiff-CovInfoMap ⽂文件路路径:/coverage/ hello.c ⾏行行号:?? Diff 类型:修改⽂文件 执⾏行行次数:1 历史路路径:空 函数名:空 List<DiffBlockInfo>: 起始删除⾏行行号:11 删除⾏行行数:1 执⾏行行次数:1 起始增加⾏行行号:11 函数名:空 增加⾏行行数:2 ⾏行行号:?? 执⾏行行次数:1 函数名:空 起始删除⾏行行号:5 删除⾏行行数:2 …… …… 起始删除⾏行行号:18 新增⾏行行数:2
33. 覆盖率合并 AD-CovInfoMap ADiff-CovInfoMap D-CovInfoMap ⾏行行号:10 ⾏行行号:10 执⾏行行次数:1 执⾏行行次数:1 函数名:空 函数名:空 ⾏行行号:13 ⾏行行号:12 执⾏行行次数:1 执⾏行行次数:1 函数名:空 函数名:空 ⾏行行号:10 执⾏行行次数:2 函数名:空 ⾏行行号:12 执⾏行行次数:1 函数名:空 ⾏行行号:13 执⾏行行次数:1 函数名:空 …… ……
34. 数据可视化 覆盖率报表 数据可视化 消息推送
35. 案例例分析
36. 覆盖率数据分析 ⽬目录覆盖率信息 YZHDBalancePaymentViewController 代码⽬目录 ⽂文件覆盖率 YZHDBalancePaymentViewController.m
37. 覆盖率异常 代码覆盖率异常
38. 增量量覆盖率异常 ?
39. 覆盖率的意义 覆盖率⾼高 != 测试质量量⾼高 覆盖率异常 ~= 场景漏漏测(边界和分⽀支)+ 代码冗余
40. 规划 开发⾃自测的质量量评估和提交限制 Code review的辅助信息 更更多维度的数据分析报告
41. QA

Home - Wiki
Copyright © 2011-2024 iteam. Current version is 2.137.1. UTC+08:00, 2024-11-11 08:31
浙ICP备14020137号-1 $Map of visitor$