关于单测,从测试来看代码编写
Posted 安然_随心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于单测,从测试来看代码编写相关的知识,希望对你有一定的参考价值。
最近写了一周多的单测,感触颇多,这里把自己的体会说一下
1. 业务背景现状
在业务开发时,是有单测的,但是单测比较简单,一个业务入口,写了一个单测函数,从头测到尾。
//如 一个给用户数据回放的功能
func RePlayDataLine(ctx , request)
.....
//对应的单测
func Test_RePlayDataLine(t *testing.T)
最基本的业务有两条主流程,回放成功和回放失败,数据回滚。在上线前自测时,先构造“回放成功 ”的数据,运行单测,符合预期。然后 将之前的 测试数据,修成,使之服务构造“回放失败,数据回滚“的条件。 一句话描述,一个单测,测试多个场景,共用一份数据。
2. 问题
当要进行整体测试时,一个 单测只能跑到一个情况,大量的场景测试不到。另外,只有一个入口的整体单测,内部很多函数覆盖不到。
3. 改进
原则上是:一个case ,补充一个单测;一个函数,对应一个单测。
但是做起来远比说起来难。在补单测时发现代码存在许多问题:
3.1 函数参数存在许多的复杂的输入参数
函数参数存在许多的复杂的输入参数;业务运行的时候,从上一个流程到下一个流程,为了传参简单,直接将上层使用的数据接口传入。
例如:
type FuncAData struct//许多的内部结构
Member1
Member2
Member3
//函数A, 在A 中构造使用了数据 data,然后将data 做为参数传入到 函数B
func FuncA()
data:=constructFuncAData(arg1,arg2)
......
FuncB(data)
//函数B
func FuncB(data FuncAData)
在写函数B 的测试函数时,需要构造输入参数FuncAData类型的输入参数,但是 FuncAData 的结构过于复杂。其实可以 只填充FuncAData 的部分成员,**但是光看 函数声明,无法知道使用了FuncAData 中的那一部分 **
其实将函数参数定义成一个很复杂的结构体,你不是用到了里面的所有的成员,这样做法不是很好。函数功能、和参数都应该是简洁明了的。
你可以会这样说:如果把FuncAData 展开,把用到的拿出来,可能参数列表比较长。这种情况下,你可以定义一个新的结构体,这个新的结构题包含了所有需要的输入参数,这样调用方调用也很简洁。
3.2 单个函数过长,功能过于复杂
补单测时,发现许多的函数都非常长。函数过于复杂、过于长,如果测试在前几行代码就返回了,后面大量的代码就无法测试。
而且,为了能够构造一直能运行到最后的代码,需要清楚的知道所有的逻辑,才能构造出测试数据。
正确的代码应该是函数单一,一个函数进入判定是否输入有误,没有错误,完成一到两件事,然后返回操作结果。
不要怕函数写的多,现在的服务器不是一起的配置,堆栈不在乎一两个函数调用栈的空间。
3.3 调用链过于长
补单测时,看函数的声明,发现构造一种数据,跑单测确返回错误。仔细分析时,往上面看了N层 上层调用链了,发现进入该场景的业务数据都一个一个特点,不允许当天存在某一个数据。而我构造的刚好 在当天存在这种数据,所以返回错了。
但是光看函数声明,看不出来,这种校验又在后面,什么样的数据是非法的 不是一目了了。而且又没有代码注释。
这种情况,需要分析所有的上层调用链,这种情况,不是很熟悉业务的同学,很容易构造错数据,然后还以为代码又问题呢
换一个场景:如果是发现在函数某一处返回了错误,与预期不符,你需要分析是那一步出错,调用链过长,你能快速的定位么?
3.4 函数中用到的数据出处过多
存在一些函数实现特点如下:
- 通过rpc 或数据库 取数据,取得两个数据源后,进行几个操作;
- 然后又通过rpc 或数据库 取另外一些数据,在进行几个动作;
- 。。。。
这种代码补单测来,需要读完所有的代码,才能把完整的数据构造完。
这种方式肯能性能比较高,某些case ,不需要后续的数据操作,就无需重新获取数据。但是程序最好能够一次把数据拿完,然后统一处理数据,这样的代码更好维护。
总结
好的代码应该是易于测试的,你容易写代码,说明你的代码以后也好维护。想一想,如果是对业务不熟的人维护你的代码,代码参数复杂,函数过长、调用链深,他肯定要话很长的时间来熟悉你的代码
以上是关于关于单测,从测试来看代码编写的主要内容,如果未能解决你的问题,请参考以下文章