单元测试编写原则
测试文件必须以 _test.go 结尾。
测试文件包名要和被测试函数所在包一致。
必须引入 testing 包。
单元测试函数必须以 Test 开头。
使用案例函数必须以 Example 开头,且有预期 Output 注释。
基准测试函数必须以 Benchmark 开头。
常用测试命令
# 单元测试 go test \ -v \ # 显示详细信息 -run=. # 只运行名称符合正则的单元测试和案例 -count=2 \ # 指定每个测试运行次数 mailer_test.go # 基准测试 go test \ -v \ # 显示详细信息 -bench=. \ # 指定待测试的 Benchmark 的正则匹配 -benchtime=5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置 -count=2 \ # 指定每个测试运行次数 -benchmem \ # 显示内存分配 benchmark_test.go # 编译测试文件 go test \ -c \ # 编译测试文件 -o /tmp/gotest # 指定编译输出的文件位置,不指定则为 ./<package-name>.test # 使用编译的测试文件进行单元测试 ./<package-name>.test \ -test.v \ # 输出详细信息 -test.run . \ # 只运行名称符合正则的单元测试和案例 -test.count 2 # 指定每个测试运行次数,默认 1 # 使用编译的测试文件进行基准测试 ./<package-name>.test \ -test.v \ # 输出详细信息 -test.bench . \ # 只运行名称符合正则的基准测试 -test.benchmem \ # 基准测试时输出内存分配 -test.benchtime 5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置 -test.count 2 # 指定每个测试运行次数,默认 1 # 覆盖率测试与分析 go test -coverprofile=c.out go tool cover -html=c.out # 基准测试与分析 sudo apt install graphviz -y go test -bench . -cpuprofile cpu.out go tool pprof cpu.out (pprof) web
案例
add.go
package main
import "fmt"
type Number struct {
a int
b int
}
func (num *Number) Multi() int {
return num.a * num.b
}
func main() {
num := Number{
a: 2,
b: 6,
}
fmt.Println(num.Multi())
}add_test.go
package main
import (
"fmt"
"testing"
)
// 单元测试
func TestNumber_Multi(t *testing.T) {
type fields struct {
a int
b int
}
tests := []struct {
name string
fields fields
want int
}{
{name: "1", fields: fields{a: 0, b: 2}, want: 0},
{name: "2", fields: fields{a: 3, b: -2}, want: -6},
{name: "3", fields: fields{a: 5, b: 9}, want: 45},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
num := &Number{
a: tt.fields.a,
b: tt.fields.b,
}
if got := num.Multi(); got != tt.want {
t.Errorf("Multi() = %v, want %v", got, tt.want)
}
})
}
}
// 使用案例
func ExampleNumber_Multi() {
num := Number{
a: 2,
b: 6,
}
fmt.Println(num.Multi())
// Output:
// 12
}
// 基准测试
func BenchmarkNumber_Multi(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
num := &Number{
a: 33,
b: 20,
}
if num.Multi() != 660 {
b.FailNow()
}
}
}int 与 string 类型互相转换的基准测试
// strconv_test.go
package test
import (
"fmt"
"strconv"
"testing"
)
func BenchmarkSprintf(b *testing.B) {
n := 10
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
fmt.Sprintf("%d", n)
}
}
func BenchmarkItoa(b *testing.B) {
n := 10
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.Itoa(n)
}
}
func BenchmarkFormatInt(b *testing.B) {
n := int64(10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.FormatInt(n, 10)
}
}
func BenchmarkAtoi(b *testing.B) {
n := "10"
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.Atoi(n)
}
}
func BenchmarkParseInt(b *testing.B) {
n := "10"
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
strconv.ParseInt(n, 10, 0)
}
b.StopTimer()
}测试结果
# -4 表示使用 4 核心 | CPU 基准测试次数 | 单次测试使用时间 | 单次测试处理的字节数 | 总的分配内存的次数 $ go test -bench=. goos: linux goarch: amd64 pkg: learn/test BenchmarkSprintf-4 6880945 172 ns/op 16 B/op 2 allocs/op BenchmarkItoa-4 183639746 6.36 ns/op 0 B/op 0 allocs/op BenchmarkFormatInt-4 180059316 6.38 ns/op 0 B/op 0 allocs/op BenchmarkAtoi-4 100000000 10.3 ns/op 0 B/op 0 allocs/op BenchmarkParseInt-4 36338739 29.6 ns/op 0 B/op 0 allocs/op PASS ok learn/test 8.164s
+= 与 buffer 字符串连接基准测试
// 利用 += 连接
func BenchmarkConcatStringByAdd(b *testing.B) {
chars := []string{"1", "2", "3", "4", "5"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ret := ""
for _, char := range chars {
ret += char
}
}
b.StopTimer()
}
// 利用 buffer 连接
func BenchmarkConcatStringBytesBuffer(b *testing.B) {
chars := []string{"1", "2", "3", "4", "5"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
for _, char := range chars {
buf.WriteString(char)
}
}
b.StopTimer()
}测试结果
# 使用 -benchmem 参数等同于在测试程序中调用 b.ReportAllocs() $ go test -bench=. -benchmem goos: linux goarch: amd64 pkg: test BenchmarkConcatStringByAdd-4 4398699 278 ns/op 16 B/op 4 allocs/op BenchmarkConcatStringBytesBuffer-4 7655295 154 ns/op 64 B/op 1 allocs/op PASS ok test 2.844s
本文标题:golang 单元测试
版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
相关文章
上一篇:golang iota 笔记