- |导语编写了大量的单元测试,覆盖率和稳定性提升的同时,却忽略了单测的目的性。我们无法衡量这些单测用例是否可以在问题发生的时候真正起到作用。变异测试(Mutation Testing)通过模拟典型应用错误,或者是强制产生有效地测试来定位测试用例设计中的弱点。本文将详细介绍看点信息流Go后台如何提升单测有效性的过程和结果。同时助力EPC研发模式变革成功。
背景
为什么要评估测试用例的有效性?
基于故障复盘的模式成本太高,单测被认为是一种形式,无法有效起到作用。我们希望能够主动创造问题来评估测试用例的有效性,并可以根据发现的问题改进我们的单测用例。
主要成果
- 用例有效性从原来的31.77分(低于平均分9.73分)提升到目前53.78分(高于平均分12.28分)
- 在满足EPC覆盖率和稳定性要求的同时,单测质量整体40%左右
- 加快变异测试执行效率
- 推动单元测试往写好方向发展,提高单测发现问题能力
- 协助测试用例设计
原理
- 评估方法 当业务代码出现问题的时候,测试用例可以发现这个问题,就认为这一组测试用例是有效的 当业务代码出现问题的时候,当测试用例覆盖了这些代码,且没能发现这个问题,就认为这一组测试用例是无效的
- 计算公式 测试用例有效性 = 被发现的问题数 / 出现问题的总数
- 变异规则 详细变异规则:https://github.com/stryker-mutator/stryker-handbook/blob/master/mutator-types.md
平台与任务(后台)
本地调试
二进制安装(推荐)
从发布页面下载对应系统的最新版本二进制文件 拷贝bin/mugo文件到GOPATH/bin中
源码安装
代码语言:javascript复制确认你已安装Go 1.13
go version
go version go1.13
将MuGo克隆到〜/MuGo
git clone http://git.code.oa.com/MutationTest/MuGo.git 〜/MuGo
编译
cd 〜/MuGo && make
在你的$PATH中添加〜/MuGo/bin以访问mugo命令行程序
对于bash:
echo 'export PATH="$HOME/MuGo/bin:$PATH"' >> ~/.bash_profile
对于Zsh:
echo 'export PATH="$HOME/MuGo/bin:$PATH"' >> ~/.zshrc
重新启动shell,以便PATH更改生效
快速开始
以下命令使用所有可用的变异体对MuGo项目进行变异测试
代码语言:javascript复制# 确保该目录可执行go test命令
cd [your project]
# 运行所有测试
mugo run ./... --debug
# 基于覆盖率运行所有测试
mugo run ./... --debug --enable-coverag
# 选择不同的文件夹进行变异
mugo run ./[name]/... --debug --enable-coverage
注意: 变异测试首先会执行单元测试,支持gotest和goconvey编写的测试用例。运行变异测试的前提是单元测试全部通过。
变异测试准确度优化
问题描述
- 存在很多无效变异体
- 包含大量无需变异的文件
优化方案
- 根据覆盖率来进行用例有效性得分,可以更有针对性发现用例存在的问题。
- 优化变异文件索引 由于存在一些无需测试的文件,这些文件不需要进行变异。所以根据正则表达式或者文件夹匹配来更加精准的对变异文件筛选,这样会使得得分更加有针对性。
- 优化变异路径 由于存在很多不需要变异的文件,导致出现很多无效的变异体。这部分是需要排除出去,以提高有效性。
- 关闭无效变异算子 变异算子的类型与项目关系十分密切。针对信息流后台项目statement/remove、statement/update这两种算子存在大量无效变异体。所以在将其排除。
- 去除log函数 针对log函数变异的变异体,无法进行验证,会严重影响有效性。
变异测试运行效率提升
问题描述
- 变异测试需要消耗cpu的大量资源。
- 随着时间随着需要变异的文件数量和测试数量增加。
- 较多的测试用例项目需要花费好几个小时。
优化方案
- 通过蓝盾流水线,执行变异测试
接入前期可以选择调试模式,等待稳定之后切换到正式模式。触发方式优先选择定时触发或者手动触发,后面可以加入事件触发。
- 合理安排流水线并行测试 在本地先将需要进行变异测试的项目的运行时间统计起来,根据运行时间进行流水线并行分配。
- 分布式运行 建议不要全部进行分布式,推荐选择较长时间的任务
优化效果
优化后,已经可以在流水线稳定运行,优化前后流水线时间对比:
流水线 | 优化前耗时 | 优化后耗时 |
---|---|---|
1 | 54min | 21min |
2 | 23min | 9min |
变异体分析
有效变异体
- 存在条件语句未考虑
- 返回err没有覆盖
- 没有覆盖条件位置
- 存在一定测试用例逻辑条件遗漏
- 缺少返回覆盖
- 内部变量可以根据mock的入参进行校验(防止无效参数)
无效变异体
1. 变异体位于Mock函数中
概述:如图所示makeUserInfoFromFields是mock函数,本不希望产生任何结果,所以变异体选择该位置是无效的。 解决方法:在平台中标记该变异体为无效。也可以将其加入到block设置中。
2. 变异内容为内部变量
条件语句判断包含内部变量
存在内部变量赋值
3. 变异体为log语句
4. 无效条件语句(仅包含log语句)
5. 变异体位于无赋值语句的函数中
找到单测用例中的问题
根据变异测试结果和变异体有效性分析,总结有如下问题需要改进。
1. True Returns
变异体将返回值从True变为False,并发现项目并没有相关单测用例。 解决方法:将所有有返回值地方均做单测覆盖。
2. False Returns
变异体将False改为True。 解决方法:将所有有返回值地方均做单测覆盖。
补充相关测试用例
3. Value Change
变异体改变操作符,导致变量值改变。 解决方法:可以增加结构体用于表示测试结果,在里面断言变量数值是否满足期望,这样就可以把这个变异体消灭掉。
4. Equality Change
变异体将大于改成大于等于,变异体存活,说明测试用例设计未考虑边界值。 解决方法:补充边界值dataField.puin=0的情况
5. Switch Case
变异体位于条件语句中。
解决方法:在Mock函数中使用stmock.Eq()
进行输入参数验证。
6. 逻辑判断
逻辑判断时存在多种组合,当前测试用例并没有全面覆盖,导致变异体存活。 解决方法:关注条件语句中逻辑判断位置,有针对性设计单测用例。 Case1:
Case2:
Case3:
Case4:
Case5:
7. 已覆盖函数,出现大量存活变异体
该函数在其他函数中存在调用,所以在覆盖率统计时被算作已覆盖,但无测试用例来检验该函数。 解决方法:新增单测用例
8. 赋值语句
变异体出现在赋值语句中,但断言并未包含此部分。 解决方法:增加此部分断言
9.边界值问题
变异体出现在边界值位置,但测试用例数值随意,未使用边界值设置。 解决方法:单测用例数据根据边界值进行设定。如图所示,添加该用例后即可杀死变异体。
Case1:
Case2:
10.数值计算:
变异体通过变换运算符出现在某一个数值计算中。 解决方法:在测试用例中需要对数值进行确认。
case1:
11.条件语句遗漏
变异体检测出条件语句存在遗漏分支。 解决方法:增加遗漏分支的覆盖与断言。
12.原函数返回值全部相同
可以新增内部参数变化进行判断。
执行方案
根据以上结果有效性以及单测问题,使用如下优化方案。分析测试用例中存在的问题,参考问题单测改进方法来进行单测质量的提升。同时,流水线每周定时3次单测质量检测,观察单测质量变化。其中用例编写是基础,结果反馈是对用例编写起到指导作用。
目前成果
通过变异测试,目前信息流后台9个仓库单测用例有效性均有明显提高。根据变异测试暴露出来的问题,有针对性的改进测试用例。用例有效性从原来的31.77分(低于平均分9.73分)提升到目前53.78分(高于平均分12.28分)。在满足EPC覆盖率和稳定性要求的同时,单测质量整体40%左右。
总结
本文对腾讯看点信息流后台自动化测试实践进行了阶段性的总结。对变异测试工具、本地运行、平台和流水线搭建、变异体分析、单测用例优化进行了详细的描述。通过尝试变异测试在满足EPC要求的前提下,对自动化用例的有效性进行提升。通过变异测试推动单元测试往写好方向发展,提高单测发现问题能力。截止目前,变异测试对信息流后台Go项目单测质量和有效性有明显的提升效果。
参考资料: http://mutation.oa.com/docs/ https://github.com/theofidry/awesome-mutation-testing
注:图片均来源于网络,无法联系到版权持有者。如有侵权,请联系后台做删除处理。