为什么要写单元测试

以前写程序的时候,一般不写测试,阅读开源代码遇到测试也都是跳过不读。调试的时候一半都是手动输入测试数据,在代码里打印 log 信息。实际上重复性的工作很多,这一部分是可以用单元测试来做的。

另外,当项目比较大的时候,一般都是把项目分割成几个模块来写的。可以分别保证各个模块的正确性,最后再把各个项目组合起来。这时候也需要单元测试,增强可维护性。

人的记忆力和思考能力毕竟是有限的,并不一定能马上想到边界条件和 bug 可能出现的地方,当代码发生更改,边界条件可能就改变了,程序可能会跑不通,这时跑一下测试代码,可以更快发现问题。

测试保证程序是可运行的,运行结果是正确的,使问题及早暴露,便于问题的定位解决。而性能测试则关注程序在高并发的情况下的稳定性。

单元测试也可以方便读代码的人读懂,通过测试代码可以更快了解这个项目到底是干嘛的、该如何用。

怎么写单元测试

单元测试

  • go语言自己有一个轻量级的测试框架 testing和命令go test,可用来进行单元测试和性能测试。

  • 测试文件用 xxx_test.go命名,测试函数命名为TestXxxTest_Xxx

  • 在终端中输入 go test,将对当前目录下的所有xxx_test.go文件进行编译并自动运行测试。

  • 测试某个文件,要带上被测试的原文件

   go test xxx.go xxx_test.go

  • 测试某个方法:go test -run='Test_Xxx'

  • go test -v 则输出通过的测试函数信息

如对以下代码进行测试:

package test

func Fibonacci(n int) int{
	if n==1{
		return 1
	}else if n==0{
		return 1
	}
	return Fibonacci(n-1)+Fibonacci(n-2)
}

测试代码为:

package test

import "testing"

func TestFibonacci5(t *testing.T) {
	e:=Fibonacci(5)
	if e!= 8{
		t.Fail()
	}else{
		t.Log("ok")
	}
}

func TestFibonacci8(t *testing.T) {
	e:=Fibonacci(8)
	if e!= 34{
		t.Fail()
	}else{
		t.Log("ok")
	}
}

性能(压力)测试:

  • 性能测试的函数命名为BenchmarkXxx

  • 函数格式为func BenchmarkXXX(b *testing.B) { ... }

  • go test默认不会执行压力测试的函数,要执行压力测试需要带上参数-bench指定测试函数,例如go test -bench=.*表示测试全部的压力测试函数。

  • 文件名也必须以_test.go结尾

一个示例压测代码:

package test

import (
	"testing"
)

func Benchmark_Fibonacci(b *testing.B) {
	for i := 0; i < b.N; i++ { //use b.N for looping
		Fibonacci( 20)

	}
	//b.Log(b.N)

}

func Benchmark_TimeConsumingFunction(b *testing.B) {
	b.StopTimer() //调用该函数停止压力测试的时间计数

	//做一些初始化的工作,例如读取文件数据,数据库连接之类的,
	//这样这些时间不影响我们测试函数本身的性能
	num:=35

	b.StartTimer() //重新开始时间
	for i := 0; i < b.N; i++ {
		Fibonacci( num)
	}
}
go test -test.bench="Benchmark_Fibonacci"

Benchmark_Fibonacci-4              30000             51647 ns/op
PASS
ok      projects/test   2.073s

以上输出说明Benchmark_Fibonacci执行了30000次,每次的执行平均时间是51647纳秒。

参考文章