Go技巧 - 如何编写可测试性好的代码
很多文章都强调了代码要具备可测试性,也讲述了如何写单元测试。但是,这些单元测试都停留于演示的水平,在复杂项目中根本无法应用,导致单元测试只停留于理论。
今天,我尝试从复杂项目的层面入手,和大家分享我的整体思路。更具体的示例会在后面讲解。
1 - 写测试的必要性
在编写测试前,你需要先真正地认可它对项目的价值。
从历史经验来看,绝大多数代码都无法被测试覆盖;而很多单元测试又因为代码变更失去效果。所以,我认为单元测试必须要覆盖的场景主要分2块:
- 通用工具库。鉴于工具库的影响面极大,对稳定性的要求极高,单元测试是一个重要且必要的质量保障手段。
- 核心业务逻辑。核心业务逻辑有2个特点:价值高、变更小。
这两块是对单元测试是价值最高的部分。当然,其余代码也可以写单元测试,但价值就没那么大了,可以按需应用。
捎带提一句,目前有不少“唱衰”单元测试价值的人。
我的态度是:大家可以对单测的价值有不同的观点,但首先,你得学会怎么写好代码与单测。如果写不好代码,又反复贬低单测,这类人往往是 为自己的能力不足找借口。
2 - 梳理代码逻辑
要编写可测试性的代码,前提是你要清晰地理解这部分代码做了什么,才能对接下来的工作做到心中有数。
梳理的方式有很多,我个人比较喜欢从面向对象的角度进行切入,体现在3个问题:
- 核心对象是什么?
- 依赖对象是什么?
- 核心对象+依赖对象是怎么工作的?
回答了这三个问题,代码逻辑的框架也基本形成了。
3 - 规划测试思路
理清了代码逻辑后,我们千万不要直接上手敲代码,否则很容易演变成了“为了写测试而写测试”,越写越迷茫。
我个人习惯在动手前,花费5分钟左右的时间对这块代码做针对性的思考:如果要写单测,我希望有哪些测试点?这个问题是为了让自己接下来的工作有更明确的方向。涉及的问题如:
- 哪部分代码最容易出错?
- 哪些外部依赖容易发生异常?
- 哪些代码对系统的价值最高?
随着测试重点的明确,编写测试代码的思路也逐渐清晰。
4 - 第零轮改造
在写单测前就改造代码,看似和单元测试的原则相悖:不是应该先写好单元测试,再针对这部分代码做优化吗?这样才能保证改造前后的一致性。
这个说法没错,但事实上,复杂的业务代码因为存在各种依赖(中间件、外部服务等),直接编写单元测试的成本极高。在面对这类可测试性极差的代码时,我更倾向于先做一轮改造 - 不妨定义为 第零轮 改造,来减少编写单测的复杂度。
代码改造的风险是很高的,我建议从2个途径降低风险:
- 用接口测试、系统集成测试等更高层面的测试来保证质量
- 第零轮改造尽量在初次提交代码时完成(也就是自己开发完后立刻优化),或者跟随小版本的迭代(大版本会提高排查的复杂度)
5 - 编写单元测试
接下来,就是常规的单元测试编写了。这部分的方法网上有很多,不再赘述,这里仅分享我的核心思路:单元测试的覆盖面可以少,但校验要尽量严格。
有些单测只是为了一个覆盖率指标,完全不去检测error
等错误,基本发现不了什么问题,也就失去了单测本身的价值。
以前我也常这么写,但随着第三步 - 规划测试思路 的引入,我会反思自己写单测的初衷:要么从一开始就放弃写单测,要么就花时间写好它、用好它。
小结
希望大家能更聚焦于2、3、4这三步,打好地基,让个人与项目从单元测试中收益更多。
关于编写可测试性好的代码,后续我也会给出具体的代码case,让大家不仅掌握方法论,也能获得实战经验。
编程思考 - 如何提高代码表达能力
很多人都觉得,程序员的形象是一个闷头敲代码的“极客”,非常内向。但是,程序员的发展离不开表达,无论是团队协作,还是展示个人能力。
我们可以在写代码时就有意识地锻炼表达能力。重点有如下3点:
- 学习英文,尽量用代码本身表述自己的逻辑
- 适当注释,尤其是背景类信息、特殊处理
- 通读代码,串联上下文逻辑,不断打磨可读性
最重要的是第三点,个人总结了3个经验:
- 通读前,要不断清空脑子里的已知信息,以全新的视角代入
- 通读时,把握主次关系,逐字逐句打磨
- 通读后,要不断反问自己,目前的实现合理吗?可以优化吗?
工作生活 - 在起伏中提高下限
生活自有起伏,我们很难把控住每个细节。就比如我的每周跑步计划,很容易因为工作情况、天气变化、身体状态等因素而波动,有时能跑个20km,而有时只有5km。
如果我从均值来制定一个目标,比如10km,那么就常常会发生达不到目标的情况,这就很容易让我有一定的心理负担,最终的效果并不会有所提高。
我最近尝试着根据下限来设计目标,如定下限5km+2km=7km作为目标,心态就有两点转变:
- 跑到5km时,会激励自己再坚持一点点就能达成目标;
- 超过目标7km后,心态会更放松,反而能坚持更久。
在生活中,往往你提高了下限,就能提高整体水平。希望我的经历能对你有帮助。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding