测试

Go自带一个轻量级的测试框架testing,以及go test命令,用于实现单元测试性能测试testing与其他语言的测试框架类似,可以针对响应函数编写测试用例,也可以基于框架写相应的压力测试用例。可以从如下几个方面保证代码质量:

  1. 确保每个函数是可运行的,并且运行结果是正确的

  2. 确保写出的代码性能,testing会展示每个测试用例执行的时间

  3. 及时发现程序设计的逻辑错误,让问题尽早暴露

使用testing测试

使用案例

目标测试包结构,该包主要提供加减乘除四个方法:

cal
├── cal.go
├── cal_test.go
└── sub_test.go

cal.go

package cal

func Plus(a, b int) int {
    return a + b
}

func Sub(a, b int) int {
    return a - b
}

func Multi(a, b int) int {
    return a * b
}

func Division(a, b int) int  {
    return a / b
}

cal_test.go(针对加乘除三个函数测试):

package cal

import (
    "testing"
)

func TestPlus(t *testing.T) {
    res := Plus(1, 2)
    e := 3 // 期望值

    if res != 3 { // 失败
        t.Fatalf("执行失败:期望值 %v, 目标值 %v ", e, res) // 发生致命错误,并格式化错误信息
    }
    t.Logf("执行成功:期望值 %v, 目标值 %v", e, res) // 打印日志
}

func TestDivision(t *testing.T) {
    res := Division(1, 2)
    t.Logf("执行成功:目标值 %v", res)
}

// 测试除零的情况
func TestDivisionByZero(t *testing.T) {
    res := Division(1, 0)
    t.Logf("执行成功:目标值 %v", res) // 打印日志
}

sub_test.go(针对减法函数测试):

package cal

import "testing"

func TestSub(t *testing.T) {
    res := Sub(1, 2)
    if res != -1 {
        t.Fatalf("执行失败:期望值 %v, 目标值 %v ", -1, res) // 失败并打印信息
    }
}

执行命令go test -v可以测试当前所在文件夹的包中的所有测试用例:

$ go test -v 
=== RUN   TestPlus  # Plus方法通过
    cal_test.go:14: 执行成功:期望值 3, 目标值 3
--- PASS: TestPlus (0.00s)
=== RUN   TestDivision # Division方法通过
    cal_test.go:19: 执行成功:目标值 0 
--- PASS: TestDivision (0.00s) # 这是运行时间
=== RUN   TestDivisionByZero # Division除0不通过
--- FAIL: TestDivisionByZero (0.00s)
panic: runtime error: integer divide by zero [recovered]
        panic: runtime error: integer divide by zero

goroutine 8 [running]:
testing.tRunner.func1.2(0x11217e0, 0x1215b40)
        /usr/local/go/src/testing/testing.go:1143 +0x332
testing.tRunner.func1(0xc000001800)
        /usr/local/go/src/testing/testing.go:1146 +0x4b6
panic(0x11217e0, 0x1215b40)
        /usr/local/go/src/runtime/panic.go:965 +0x1b9
cal.Division(...)
        /Users/yangsx/GoLandProjects/notes-golang/src/cal/cal.go:16
cal.TestDivisionByZero(0xc000001800)
        /Users/yangsx/GoLandProjects/notes-golang/src/cal/cal_test.go:23 +0x12
testing.tRunner(0xc000001800, 0x114d280)
        /usr/local/go/src/testing/testing.go:1193 +0xef
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1238 +0x2b3
exit status 2
FAIL    cal     0.353s

只打印测试失败结果

去除-v参数即可,如下测试就没有打印任何成功的测试用例:

$ go test    
--- FAIL: TestDivisionByZero (0.00s)
panic: runtime error: integer divide by zero [recovered]
        panic: runtime error: integer divide by zero

goroutine 18 [running]:
testing.tRunner.func1.2(0x11217e0, 0x1215b40)
        /usr/local/go/src/testing/testing.go:1143 +0x332
testing.tRunner.func1(0xc000082600)
        /usr/local/go/src/testing/testing.go:1146 +0x4b6
panic(0x11217e0, 0x1215b40)
        /usr/local/go/src/runtime/panic.go:965 +0x1b9
cal.Division(...)
        /Users/yangsx/GoLandProjects/notes-golang/src/cal/cal.go:16
cal.TestDivisionByZero(0xc000082600)
        /Users/yangsx/GoLandProjects/notes-golang/src/cal/cal_test.go:23 +0x12
testing.tRunner(0xc000082600, 0x114d280)
        /usr/local/go/src/testing/testing.go:1193 +0xef
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1238 +0x2b3
exit status 2
FAIL    cal     0.726s

针对某个测试文件测试

注意,一定要带上被测试的原文件:

$ go test sub_test.go  cal.go 
ok      command-line-arguments  0.421s

针对某个测试函数测试

仅测试TestSub单元测试函数:

$ go test -v -test.run TestSub
=== RUN   TestSub
--- PASS: TestSub (0.00s)
PASS
ok      cal     0.235s

使用注意事项

  1. 测试文件必须以_test.go结尾,前缀则不要求与源码文件相同

  2. 测试函数必须以Test开头,后缀通常是被测函数的名称(不是必需的),并且首字母必须大写,否则不会识别。比如TestDivisionTestDivisionByZero

  3. 测试函数的参数必须为t testing.T

  4. 测试函数体中需要打印日志的情况可以使用t.Logf方法打印

  5. 如果出现错误,也就是不符合期望值,可以调用t.Error(),t.Fatalf()等方法标记当前测试失败

  6. 如果测试过程中发生异常,测试用例失败

testing的原理

  1. go test工具将测试文件cal_test.go引入到自己的main

  2. main中调用所有的测试用例

类似如下伪代码:

func main() {
    TestXXX()
    ...
}

goconvey

https://github.com/smartystreets/goconvey

特点:

  1. 测试代码优雅简洁

  2. 直接集成Go原生测试

  3. 全自动编译测试

  4. 详细展示测试结果以及覆盖率

  5. 高可读性的命令行输出结果

  6. 半自动化书写测试用例

安装:

$ go get github.com/smartystreets/goconvey

# 或者

$ go install github.com/smartystreets/goconvey 

使用案例

目标测试文件 calc.go

package goconvey

import "errors"

func Add(a, b int) int {
	return a + b
}

func Subtract(a, b int) int {
	return a - b
}

func Multiply(a, b int) int {
	return a * b
}

func Division(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("除数不能为0")
	}
	return a / b, nil
}

创建测试文件 calc_test.go

package goconvey

import (
	. "github.com/smartystreets/goconvey/convey" // 导入convey包,使用Convey函数以及So等函数
	"testing"                                    // go testing 测试框架支持
)

func TestAdd(t *testing.T) {
	// 使用Convey方法声明一个测试的范围,主要包含 描述、以及一个用于执行测试逻辑的func()
	Convey("将两个数相加", t, func() {
		// 用于断言的通用方法,他可以接收如下三个参数
		// 1, 实际值
		// 2, 判断方式,这里是相同
		// 3, 期望值
		// 如果实际值不等于期望值,则代表测试未通过
		So(Add(1, 2), ShouldEqual, 3)
	})
}

func TestSubtract(t *testing.T) {
	Convey("将两个数相减", t, func() {
		So(Subtract(1, 2), ShouldEqual, -1)
	})
}

func TestMultiply(t *testing.T) {
	Convey("将两个数相乘", t, func() {
		So(Multiply(1, 2), ShouldEqual, 2)
	})
}

func TestDivision(t *testing.T) {
	Convey("将两个数相除", t, func() {
		// 不同的测试情况,请使用嵌套的Convey方法
		Convey("除数不为0", func() {
			r, err := Division(4, 2)
			So(r, ShouldEqual, 2)
			So(err, ShouldBeNil)
		})
		Convey("除数为0", func() {
			_, err := Division(4, 0)
			So(err, ShouldNotBeNil)
		})
	})
}

断言函数索引

Assertions · smartystreets/goconvey Wiki (github.com)

使用go test原生命令测试

$ go test    
# 四个测试方法,共有四个测试结果
.   # 代表一个So判断
1 total assertion 

. 
2 total assertions

.
3 total assertions

... # 代表三个So判断
6 total assertions

PASS
ok      notes-golang/goconvey   0.693s

使用go test -v,查看更详细的结果:

$ go test -v                  
=== RUN   TestAdd

  将两个数相加   # ✔ 代表So的通过次数


1 total assertion

--- PASS: TestAdd (0.00s)
=== RUN   TestSubtract

  将两个数相减 


2 total assertions

--- PASS: TestSubtract (0.00s)
=== RUN   TestMultiply

  将两个数相乘 


3 total assertions

--- PASS: TestMultiply (0.00s)
=== RUN   TestDivision

  将两个数相除 
    除数不为0 ✔✔  # ✔ 代表So的通过次数,有可能为✘,就代表So不通过的个数
    除数为0 


6 total assertions

--- PASS: TestDivision (0.00s)
PASS
ok      notes-golang/goconvey   0.236s

使用goconvey提供的工具测试

$ goconvey                                      
2021/12/15 10:30:32 goconvey.go:116: GoConvey server: 
2021/12/15 10:30:32 goconvey.go:121:   version: v1.7.2      gomodifytags   goplay         gotests                                                            
2021/12/15 10:30:32 goconvey.go:122:   host: 127.0.0.1
2021/12/15 10:30:32 goconvey.go:123:   port: 8080
2021/12/15 10:30:32 goconvey.go:124:   poll: 250ms
2021/12/15 10:30:32 goconvey.go:125:   cover: true
2021/12/15 10:30:32 goconvey.go:126: 
2021/12/15 10:30:32 tester.go:19: Now configured to test 10 packages concurrently.
2021/12/15 10:30:32 goconvey.go:243: Serving HTTP at: http://127.0.0.1:8080
2021/12/15 10:30:32 goconvey.go:146: Launching browser on 127.0.0.1:8080
2021/12/15 10:30:32 integration.go:122: File system state modified, publishing current folders... 0 3279069840
2021/12/15 10:30:32 goconvey.go:159: Received request from watcher to execute tests...
2021/12/15 10:30:32 goconvey.go:154: 
2021/12/15 10:30:32 executor.go:69: Executor status: 'executing'
2021/12/15 10:30:32 coordinator.go:46: Executing concurrent tests: notes-golang/goconvey
2021/12/15 10:30:32 parser.go:24: [passed]: notes-golang/goconvey
2021/12/15 10:30:32 executor.go:69: Executor status: 'idle'

测试覆盖率,点击可以查看已覆盖以及未覆盖的go代码:

全自动编译测试

  1. 服务启动后,会在后台监测代码的变动并重新进行单元测试。如果自动编译测试不生效可以使用刷新按钮强制刷新

  2. 如果想要暂停自动编译测试,可以使用暂停按钮暂停

测试失败的展示

半自动化书写测试用例

最后更新于