go 单元测试

基础

Go语言中的测试依赖go test命令。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。

在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数

类型格式作用
基础测试前缀为Test测试程序的逻辑是否正确
性能测试前缀为Benchmark测试程序的性能
提供示例前缀为Example为程序提供示例

go test中可用的参数有:
go test是go语言自带的测试工具,其中包含的是两类,单元测试和性能测试

通过go help test可以看到go test的使用说明:

格式形如: go test [-c] [-i] [build flags] [packages] [flags for test binary]

参数解读:

-c : 编译go test成为可执行的二进制文件,但是不运行测试。

-i : 安装测试包依赖的package,但是不运行测试。

关于build flags,调用go help build,这些是编译运行过程中需要使用到的参数,一般设置为空

关于packages,调用go help packages,这些是关于包的管理,一般设置为空

关于flags for test binary,调用go help testflag,这些是go test过程中经常使用到的参数

-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。

-test.run pattern: 只跑哪些单元测试用例

-test.bench patten: 只跑那些性能测试用例

-test.benchmem : 是否在性能测试的时候输出内存情况

-test.benchtime t : 性能测试运行的时间,默认是1s

-test.cpuprofile cpu.out : 是否输出cpu性能分析文件

-test.memprofile mem.out : 是否输出内存性能分析文件

-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件

-test.memprofilerate n : 内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。如果你设置为0,那就是不做打点了。

你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且对每个内存块的分配进行观察。

-test.blockprofilerate n: 基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下

-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS。

-test.timeout t : 如果测试用例运行时间超过t,则抛出panic

-test.cpu 1,2,4 : go test -cpu=1,2,4 将会执行 3 次,其中 GOMAXPROCS 值分别为 1,2,和 4。

-test.short : 将那些运行时间较长的测试用例运行时间缩短

利用单元测试来测试斐波那契数列的递归和非递归版本

// 斐波那契数列:后一个数等于前面两个数的和
func fb_recursion(num int) int {
	if num <= 2 {
		return 1
	}
	return fb_recursion(num - 1) + fb_recursion(num -2)
}

func fb_norecursion(num int) int {
	l := make([]int, num+1)
	for i := 1; i <= num; i++ {
		if i <= 2 {
			l[i] = 1
		} else {
			l[i] = l[i-1] + l[i-2]
		}
	}
	return l[num]
}

type FbTestCase struct {
	num int
	expect int
}

var fbTestCases = map[string]FbTestCase {
	"1": {num: 1, expect: 1},
	"2": {num: 2, expect: 1},
	"3": {num: 3, expect: 2},
	"4": {num: 4, expect: 3},
	"5": {num: 5, expect: 5},
	"9": {num: 9, expect: 34},
}

// 基准测试
func Test_fb_recursion(t *testing.T) {
	for name,tc := range fbTestCases {
		// 使用t.Run执行子测试
		t.Run(name, func(tt *testing.T) {
			output := fb_recursion(tc.num)
			if output != tc.expect {
				tt.Errorf("expect: %d, but output: %d", tc.expect, output)
			}
		})
	}
}

func Test_fb_norecursion(t *testing.T) {
	for name,tc := range fbTestCases {
		// 使用t.Run执行子测试
		t.Run(name, func(tt *testing.T) {
			output := fb_norecursion(tc.num)
			if output != tc.expect {
				tt.Errorf("expect: %d, but output: %d", tc.expect, output)
			}
		})
	}
}

go test -run '.*fb.*' -v
结果:

=== RUN   Test_fb_recursion
=== RUN   Test_fb_recursion/1
=== RUN   Test_fb_recursion/2
=== RUN   Test_fb_recursion/3
=== RUN   Test_fb_recursion/4
=== RUN   Test_fb_recursion/5
=== RUN   Test_fb_recursion/9
--- PASS: Test_fb_recursion (0.00s)
    --- PASS: Test_fb_recursion/1 (0.00s)
    --- PASS: Test_fb_recursion/2 (0.00s)
    --- PASS: Test_fb_recursion/3 (0.00s)
    --- PASS: Test_fb_recursion/4 (0.00s)
    --- PASS: Test_fb_recursion/5 (0.00s)
    --- PASS: Test_fb_recursion/9 (0.00s)
=== RUN   Test_fb_norecursion
=== RUN   Test_fb_norecursion/4
=== RUN   Test_fb_norecursion/5
=== RUN   Test_fb_norecursion/9
=== RUN   Test_fb_norecursion/1
=== RUN   Test_fb_norecursion/2
=== RUN   Test_fb_norecursion/3
--- PASS: Test_fb_norecursion (0.00s)
    --- PASS: Test_fb_norecursion/4 (0.00s)
    --- PASS: Test_fb_norecursion/5 (0.00s)
    --- PASS: Test_fb_norecursion/9 (0.00s)
    --- PASS: Test_fb_norecursion/1 (0.00s)
    --- PASS: Test_fb_norecursion/2 (0.00s)
    --- PASS: Test_fb_norecursion/3 (0.00s)
PASS
ok      mytest/testperformancetest      0.068s


测试支持的函数

使用go doc testing.T查询文档

package testing // import "testing"

type T struct {
        // Has unexported fields.
}
    T is a type passed to Test functions to manage test state and support
    formatted test logs.

    A test ends when its Test function returns or calls any of the methods
    FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as
    the Parallel method, must be called only from the goroutine running the Test
    function.

    The other reporting methods, such as the variations of Log and Error, may be
    called simultaneously from multiple goroutines.

func (c *T) Cleanup(f func())
func (t *T) Deadline() (deadline time.Time, ok bool)
func (c *T) Error(args ...interface{})
func (c *T) Errorf(format string, args ...interface{})
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...interface{})
func (c *T) Fatalf(format string, args ...interface{})
func (c *T) Helper()
func (c *T) Log(args ...interface{})
func (c *T) Logf(format string, args ...interface{})
func (c *T) Name() string
func (t *T) Parallel()
func (t *T) Run(name string, f func(t *T)) bool
func (c *T) Skip(args ...interface{})
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...interface{})
func (c *T) Skipped() bool
func (c *T) TempDir() string

go doc testing testing.T.Cleanup 查询Cleanup的方法描述

func (c *T) Cleanup(f func())
    Cleanup registers a function to be called when the test and all its subtests
    complete. Cleanup functions will be called in last added, first called
    order.

覆盖率测试

go test -cover -run '.*fbp.*' -coverprofile='c.out' -coverpkg='mytest/fb'
-cover代表进行覆盖率测试,-coverprofile代表将结果存储到c.out,-coverpkg用于指定覆盖率测试的目标包

PASS
coverage: 100.0% of statements in mytest/fb
ok      mytest/testperformancetest      0.229s

生成的测试结果,可使用
go tool cover -html='c.out' -o coverage.html来转换成可视化的html:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvCQtHoO-1649311894502)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202203122232143_image.png)]

示例函数

func Example_fb_norecursion() {
	fmt.Println(fb.Fb_norecursion(1))
	fmt.Println(fb.Fb_norecursion(3))
	// Output:
	// 1
	// 2
}

示例函数也可以当作测试函数进行运行。运行结果需要与Output一致:
go test -run Example_fb_norecursion

=== RUN   Example_fb_norecursion
--- PASS: Example_fb_norecursion (0.00s)
PASS
ok      mytest/testperformancetest      0.071s


示例函数能够作为文档直接使用,例如基于web的godoc中能把示例函数与对应的函数或包相关联。

示例函数只要包含了// Output:也是可以通过go test运行的可执行测试。

  split $ go test -run Example
  PASS
  ok      github.com/pprof/studygo/code_demo/test_demo/split       0.006s

示例函数提供了可以直接运行的示例代码,可以直接在golang.org的godoc文档服务器上使用Go Playground运行示例代码。下图为strings.ToUpper函数在Playground的示例函数效果。
示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-valNxzC3-1649311894503)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202203122235955_image.png)]

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值