软件的信心

顶级体验的过山车系统

要上线的系统你坐先

心头之问

面对功能强大、日益复杂,给人顶级体验的”过山车“系统,其安全保障也是非常之事,开发者对代码的信心来自哪里?

在给”过山车“添砖加瓦交付的时候,你有如下经历吗?

  • 上线也像坐过山车
  • 你亲手打造的过山车,自己是否亲身体验过?
  • 是否对测试同事说:你们先上去坐坐看,遇到问题再下来找我?

当自己不敢坐,代码要靠测试老师兜底,就说明工作还有待提升空间。反之说明,你作为一个开发者你已经相当优秀了。

单测篇

如何建立对自己代码的信心

借外力

启动前做充分检查:代码评审、让资深人员 (老法师) 帮把关,及时发现问题

练内功

零部件测试(单元测试),保证每一部分在各种场景下都可以正常工作或异常处理得当

对单测的看法

质疑声

常有的声音,尤其在业务变化快的互联网行业:

  1. 有必要做吗?
  2. 做到多少合适?
  3. 现在没做不也挺好吗

实际遇到线上问题需要消耗多少工时?保守估计下面一套下来几个人日是底线值了。

  1. 找bug,修bug上线
  2. 修复数据问题
  3. 复盘闭环规避 (文书工作)

所以常说:发现的越晚,修复耗时越久。编码阶段分钟级、功能测试小时级,发布后就不可控了。【缺陷发现越到交付流程后端,修复成本就越高】

定性来看

写单测抱怨耗时多、可用维护也耗时,但集成测试阶段跟踪调试问题,解决时效被拖长。

单测高覆盖的长周期好处

每次代码改动后可提前发现改动引发的其它关联问题,软件质量更有保障,节省后续同事的精力,拉长周期看效率更高

单测的FIRST原则

看看就好,完全犯不着记

  • FAST,快速
  • Independent,独立
  • Repeatable,可重复
  • Self-Validating,自我验证
  • Thorough/Timely,彻底、及时

现状及特点

现状

  • 从行业特点看:传统行业软件(ERP,CRM等)单测覆盖率至少达到80%以上,互联网行业较低,一般低于50%,大部分没有
  • 从软件特点看:用户量较大的软件(工具类,中间件等)基础软件覆盖率相对较高,80%以上,需求变化快的业务类软件较低
  • 从开发习惯看:国外开发的软件较高,多数开源软件覆盖率至少60%以上。国内开发者多数未养成习惯

例如我所在的项目环境全量代码单测分支覆盖率普遍看低于30%,质量主要靠测试兜底。这还是在考核变更代码行单测覆盖率的情况下,很多考核流于表面,只看覆盖率,不看通过率。我随处可见运行报告中,用例通过率只有50%,但依然统计生成行覆盖率,而不是中止。另外用例普遍断言宽松,实际并不能达到改动自测的效果。

痛点

  • 开发者需投入更多工作量:单测代码行数与应用功能代码至少1:1,复杂应用则更高。随着单测覆盖率的提升,每提升1%,都需要大量的用例。因为后续用例至少有80%甚至90%以上的代码运行路径是重叠的,最坏的情况是增加一个用例,只多了一个覆盖

  • 存量代码数量庞大:见上一条

  • 单测代码容易失效:单测代码需要持续维护,当业务需求引发的失效要么忽略,要么删除。如此情况下,很难维持一个较高的覆盖率指标。

归根到底,就是成本问题。

这道菜原始原料是 开发者持续投入大量精力(克制牺牲一定要见到实际好处才可持续,但这条维度在任何公司都排不到第一位考核上,它不是直接第一线的结果指标)

自动化解药

静态代码分析

单测自动化以前主流是靠静态代码分析,找出代码隐藏的错误和缺陷,有EvoSuite,Squaretest等

  • 此类产品生成的单测行覆盖率一般可达到30%左右,代码越复杂效果越差

  • 好处是安装即可用,还支持多种开发平台:idea,eclipse,命令行等

  • 缺陷是:生成代码质量不高,覆盖率不高。一些有丰富业务语义的信息,难通过静态分析生成有效的用例代码

大模型

代码大模型也是一条路,但当前阶段没看到大范围铺开,其实飘出的尚未足够革命/实用的味道,在业务语义丰富的方法场景生成效果也不可用

  • 每次生成的结果都理解一致无法保证
  • 生成的结果需人工修订,断言需大量人工工作,使其可用
  • 无法解决系统间集成代码的自测验证用例覆盖

录制回放

将录制数据转化为单元测试用例,获得更加丰富的业务数据,而不是通过一些策略生产数据

此种方案有一个口实弊病:违反了及时性原则,应该在写代码时/提测前完成啊,后录制已经晚了。是的,它不是理论上完美的

优势

  • 存量代码收效大,能快速提升覆盖率

  • 新代码,开发本地运行程序自测,录制,而后基于生成的用例,复制,调整快速扩充,也能保证及时性

接口层级用例录制回放

调和这碗意见不一致的羹汤

函数执行逻辑中通常包含 普通函数外部系统调用两类。其中外部系统调用也含数据库操作。当下微服务时代,系统间的交付变得日益复杂,过度依赖外部服务很难保障用例执行成功。

当前单测框架多采用了mock技术,屏蔽外部服务的依赖,如mockito、powermock、spock等。通过接口录制生成单测,帮开发者回顾找到代码里的bug,通过自动化技术建立单测的信心,软件的信心。

方案优劣势

  • 正:用例编码量较少,实现速度快,业务执行路径被测试到,单测覆盖率指标不受影响

  • 反:用例失败场景定位较慢

学究派肯定会有疑问?这样做跟集成测试阶段自动化用例有啥区别?

是的,从效果上看是一样的,只不过将运行前移到了开发阶段

如何做?

每个用例方法对应一个Json/自定义格式文件[非Json反序列稳定性更有优势],记录出入参,全部外部调用的数据。

用例代码和数据均由工具自动生成。大部分代码都是在帮开发者将录制的数据组装Mock对象,这部分工作量在实际开发中是最大的,因此可大幅减轻开发者自己手工编码工作。

当需要手工扩充用例时,只需将用例方法和数据复制一份,再对数据做出调整即可制作出新的用例。

挑战:

  • 用例的采样控制,重复用例的识别与剔除。
  • 基于动态代理技术实现代码的特殊处理,如mybatis,JSF
  • 结构化数据的录制与还原,复杂泛型的还原,复杂对象的序列化和反序列化
  • 底层技术如何使接入成本降低,减少对应用的入侵

结语

写单测这部分不是空谈,我们真有在实践,效果不错

参考