最近写单元测试,知道 golang的单测覆盖率提高会比较难,没想到这么难。当提高到一定程度,有的 case就无法成型了,也就无从增加覆盖率。为何呢?思考许久,究其原因,还是被测代码属于“不可测代码”。
不可测代码有哪些特性呢?对照着手中这段代码,我也尝试归纳下:
1 代码中用了全局常量。特别是测试环境和正式环境不一样的常量。比如一个创建文件的函数,把文件所在目录常量化,那么这个函数就完全不可测了。
解决办法就是把全局常量换成全局变量。
2 代码中频繁使用隐藏 go。比如一个函start 函数,里面是启动了一个 goroutine,循环监听 channel。这个函数就很难测试,因为测试用例不是常驻的,甚至不应该是阻塞等待的,它就无法等待新开启的 goroutine运行完整。
解决办法就是想办法把 go关键字放到函数外面去。
3 链式调用。在函数内部链式调用,我们打桩都不知道打在哪个函数上,导致整个父函数很难写测试用例。
解决办法就是少用链式调用。
4 定义了一个结构体,但它的调用函数依赖各种配置项。比如一个函数如果配置了 a 值,就走 a 逻辑,否则走 b 逻辑。这些配置项没有在结构体中定义,而是调用全局方法来获取。这种配置项的 mock 需要花费极大的精力。
解决方式是把配置项作为结构体属性。当我们使用这些逻辑分支相关配置的时候,这些配置实际上就是逻辑的一部分了,它应该作为结构体的元素被表示出来。
大概能看到的几种场景就是如上了,总结下
- 错误使用全局常量
- 频繁使用隐藏 go 函数
- 频繁使用链式调用
- 频繁使用逻辑配置,且从全局获取这些逻辑配置
所以,当代码有了上述表征,就可以冠名之为,不可测代码。cr 的时候你就可以勇敢说出那句,wtf。