- 传统方法进行测试
- 单元测试
- 基本介绍
- import “testing”
- func (c *T) FailNow()
- func (c *T) Fatalf(format string, args …interface{})
- string, args …interface{})">func (c *T) Logf(format string, args …interface{})
- 代码测试
- 测试结果
- testing框架调用说明
- 注意事项
单元:一个函数
func add(n int) int {
res := 0
for i := 0; i < n; i++ {
res += i
}
return res
}
传统方法进行测试
在main函数中调用 add 函数,看看实际输出跟预期是否一致,如果一致,说明函数正确,否则说明有误。
package main
import (
"fmt"
)
func add(n int) int {
res := 0
for i := 0; i < n; i++ {
res += i
}
return res
}
func main() {
var sum int = add(5)
if sum != 10 {
fmt.Printf("add函数错误, 返回值:%v, 期望值: %v \n", sum, 10)
} else {
fmt.Printf("add函数正确, 返回值:%v, 期望值: %v \n", sum, 10)
}
}
缺点分析
- 不方便,需要在主函数中调用,这样就需要修改代码,如果项目正在运行,可能还需要停止项目
不利于管理,当需要测试多个函数或者多个模块时,都需要写在main函数,容易扰乱代码逻辑
单元测试
基本介绍
Go语言自带有一个轻量级测试框架 testing 和自带的 go test 命令来实现单元测试和性能测试。
可以基于 testing 框架写针对相应函数写测试用例,也可以写压力测试用例。通过单元测试,可以解决以下问题:
- 确保每个函数是可运行的,并且运行结果是正确的
- 确保写出来的代码性能是好的
- 单元测试能及时发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题定位解决,让程序能在高并发的情况下保持稳定
import “testing”
testing 提供对 Go 包的自动化测试的支持。通过 **go test**
命令,能够自动执行如下形式的任何函数:
func TestXxx(testing.T)
其中 Xxx 可以是任何字母数字字符串(但第一个字母不能是 [a-z]),用于识别测试例程。
在这些函数中,使用 Error, Fail 或相关方法来发出失败信号。
要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx
函数,如上所述。 将该文件放在与被测试的包相同的包中。该文件将被排除在正常的程序包之外,但在运行 “go test” 命令时将被包含。 有关详细信息,请运行 “go help test” 和 “go help testflag” 了解。
如果有需要,可以调用 T 和 *B 的 Skip 方法,跳过该测试或基准测试:
func TestTimeConsuming(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
...
}
func (c *T) FailNow()
将当前测试标识为失败并停止执行该测试,在此之后,测试过程将在下一个测试或者下一个基准测试中继续。
FailNow 必须在运行测试函数或者基准测试函数的 goroutine 中调用,而不能在测试期间创建的 goroutine 中调用。调用 FailNow 不会导致其他 goroutine 停止。
func (c *T) Fatalf(format string, args …interface{})
调用 Fatalf 相当于在调用 Logf 之后调用 FailNow 。
func (c *T) Logf(format string, args …interface{})
Log 使用与 Printf 相同的格式化语法对它的参数进行格式化,然后将格式化后的文本记录到错误日志里面。
代码测试
testcase/cal.go 文件
// 关于计算的测试用例
package main
func add(n int) int {
res := 0
for i := 0; i < n; i++ {
res += i
}
return res
}
func getSub(n1 int, n2 int) int {
return n1 - n2
}
testcase/cal_test.go
package main
import (
"testing" // 引入 testing 框架
)
// 编写测试用例
func TestAdd(t *testing.T) {
// 调用
res := add(5)
if res != 10 {
// fmt.Printf("add(5)执行错误, 返回值:%v, 期望值: %v \n", res, 10)
t.Fatalf("add(5)执行错误, 返回值:%v, 期望值: %v \n", res, 10)
} else {
t.Logf("add(5)执行正确")
}
}
func TestHello(t *testing.T) {
// fmt.Printf("t类型: %T, 值: %v \n", t, t)
t.Logf("TestHello被调用")
}
/*
go test -v
=== RUN TestAdd
cal_test.go:16: add(5)执行正确
--- PASS: TestAdd (0.00s)
=== RUN TestHello
cal_test.go:22: TestHello被调用
--- PASS: TestHello (0.00s)
PASS
ok go_code/unittest/testdemo01/testcase 6.356s
*/
testcase/sub_test.go
package main
import "testing"
// 编写测试用例
func TestGetSub(t *testing.T) {
// 调用
res := getSub(10, 3)
if res != 7 {
// fmt.Printf("add(5)执行错误, 返回值:%v, 期望值: %v \n", res, 10)
t.Fatalf("getSub(10, 3)执行错误, 返回值:%v, 期望值: %v \n", res, 7)
} else {
t.Logf("getSub(10, 3)执行正确")
}
}
测试结果
=== RUN TestAdd
cal_test.go:16: add(5)执行正确
--- PASS: TestAdd (0.00s)
=== RUN TestHello
cal_test.go:22: TestHello被调用
--- PASS: TestHello (0.00s)
=== RUN TestGetSub
sub_test.go:14: getSub(10, 3)执行正确
--- PASS: TestGetSub (0.00s)
PASS
ok go_code/unittest/testdemo01/testcase 10.916s
testing框架调用说明
注意事项
- 测试用例文件名必须以 xxx_test.go 命名(比如: cal_test.go)
- 测试用例函数名必须是 TestXxx ,testing框架内部会自动收集并调用这些函数(比如: TestAdd)
- TestXxx 函数的参数类型必须是 t *testing.T (比如: TestAdd(t *testing.T))
- 一个测试用例文件中可以包含多个测试用例函数
- go test 命令: 如果测试通过则没有log输出(不输出 t.Logf t.Fatalf ,只输出 pass 和运行时间),错误的情况下才会有log
- go test -v 会输出log
- 测试用例函数并没有放在main函数中
- PASS 表示运行成功, FAIL 表示运行失败
- 测试单个用例文件,一定要带上被测试的源文件 (比如: go test -v cal_test.go cal.go)
- 测试单个用例函数,(比如: go test -v -run TestAdd)