欢迎参观个人网站https://fangyuhui.com
开发环境GoLand,Go
版本1.12.1
提高程序质量水平,可以从写单元测试这种可操作的事情做起。
Google为了保证工程师所写程序的质量,鼓励大家多写测试代码。
凡事做记录,这样可以避免狗熊掰棒子。
大部分人过分相信自己的记忆力,以为自己能记住,但实际上很快忘了。
做记录的好处是,在记录过程中,又思考了一遍,进步得会更快。
Google每周会进行单元测试竞赛,给获胜者发放证书和奖金。单元测试达不到规定的比例,代码不允许提交。
所以,想要成为一名优秀的工程师,写好单元测试是非常重要的。
在我看来,写单元测试相当于又做了一次记录,重新梳理了思路。下次回头看代码的时候,有助于理解,也能帮助别人看清你的代码是怎么写的。
了解了单元测试的重要性,接下来,我们来看看,具体到Go语言的开发中,单元测试怎么写。
Go语言是Google开发的,因为Google要求单元测试必须覆盖100%的代码,所以,Go语言的源码中几乎包含了所有函数和方法的单元测试代码。因此,对于学习Go语言来说,从阅读源码中的单元测试代码着手,是一个非常好的学习方法。可惜,这种六脉神剑般逆天的学习方法,从来没人跟我讲过。因为国内很多人都不重视单元测试,网上的各种课程、教程也从来都不讲单元测试,我觉得这是非常不可取的。各种几天、十几天入门某某语言的课程,几乎没有讲单元测试的。但是我认为,学习Java、Go、C++、Python等语言,一开始就要学写单元测试,这样非常有助于学习,能学得更快,养成写单元测试的习惯,也极其有利于日后的工作。
写单元测试并不难,尤其是Go语言,几乎所有的外部函数和方法,都有单元测试代码,也就是说,源码中提供了所有单元测试的范例,如果不会写,阅读源码中的单元测试,照着写,这样也顺便学习了Google的代码规范。
Go语言中的单元测试,分为【测试】和【基准性能测试】,【测试】就是看一个函数或方法,写的是否正确,能否得到预期的运行结果;【基准性能测试】就是指,测试一个函数或者方法的性能或效率,比如一个排序算法,排列一个包含1000万个元素的随机数组,总耗时是多少,内存开销是多少。
我们就以源码中的sort
包为例,也就是Go语言中排序算法的包src/sort/sort_test.go
,看一个一般的测试
var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
func TestReverseSortIntSlice(t *testing.T) {
data := ints
data1 := ints
a := IntSlice(data[0:])
Sort(a)
r := IntSlice(data1[0:])
Sort(Reverse(r))
for i := 0; i < len(data); i++ {
if a[i] != r[len(data)-1-i] {
t.Errorf("reverse sort didn't sort")
}
if i > len(data)/2 {
break
}
}
}
这段代码包含哪些信息呢?
首先,是测试函数的名字TestReverseSortIntSlice
,以Test
开头,后面跟着这段测试代码的名字(不一定是待测函数的名字,可以自己起名),ReverseSortIntSlice
,反转排序int切片
,其实就是测的Reverse
函数,也就是把数组中所有元素的顺序倒过来。
该函数有个参数t *testing.T
,这是Go单元测试的固定写法,只能有一个参数,参数类型为*testing.T
,该函数没有返回值。
里面的具体实现是,先声明两个int数组,元素相同,里面有十几个整数,将第一个数组用Sort()
函数排列(即从小到大正序排列),将第二个数组用Reverse()
函数排列(即从大到小逆序排列),然后遍历两个数组,看看第二个数组中的最后一个元素是否等于第一个数组的第一个元素,依次比较,如果比到哪一个元素,发现不相等,则执行t.Errorf("reverse sort didn't sort")
这段代码。
t.Errorf()
函数的具体实现如下
// Errorf is equivalent to Logf followed by Fail.
func (c *common) Errorf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.Fail()
}
打印日志,并标记测试失败,函数继续运行,执行下一次遍历,打印的信息是自己写的"reverse sort didn’t sort",意即反转排序没有排序,表示测试失败。说明该Reverse()
函数写得有问题。没有达到预期运行结果,没有将数组的顺序反过来。
这就是一段Go语言标准的单元测试范例,其中标记测试失败的方法除了t.Errorf()
之外,还有以下几个,
// Fatal is equivalent to Log followed by FailNow.
func (c *common) Fatal(args ...interface{}) {
c.log(fmt.Sprintln(args...))
c.FailNow()
}
表示打印日志,并立即退出,与刚才的t.Errorf()
函数不同,Fatal
会在测试失败时立即退出,后面的代码不再运行了,Errorf
函数则在测试失败后继续执行,并不立即退出。
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
表示打印日志,不标记成功与否,也不退出执行。
// Skipf is equivalent to Logf followed by SkipNow.
func (c *common) Skipf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.SkipNow()
}
表示打印日志,并跳过本段测试。
如果一个测试文件中有多个测试函数,比如这种
func TestSortIntSlice(t *testing.T) {…}
func TestSortFloat64Slice(t *testing.T) {…}
func TestSortStringSlice(t *testing.T) {…}
func TestInts(t *testing.T) {…}
func TestFloat64s(t *testing.T) {…}
func TestStrings(t *testing.T) {…}
func TestSlice(t *testing.T) {…}
如果运行的是这整个文件,那么这些测试函数会依次执行,直到所有的测试函数执行完毕,不管内部是否测试失败,这些函数每一个都会被执行。
在GoLand编译器中,提供了测试包的功能,就是一次运行一个包中所有的测试文件,而通常每一个测试文件中,都有好几个测试函数,最后控制台会打印所有测试函数的测试结果。
Go语言的单元测试,并不难写,但是写单元测试非常重要,今天先到这里,下一节,再看看【基准性能测试】怎么写。
欢迎参观个人网站https://fangyuhui.com