IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Glang单元测试与基准测试 -> 正文阅读

[开发测试]Glang单元测试与基准测试

一. 测试概要

1.1 测试分类

Go语言的测试文件须以_test.go结尾,包含示例函数单元测试函数基准测试函数主测函数四种类型。

类型格式作用
示例函数ExampleFoo()提供示例文档
测试函数TestFoo(t *testing.T)测试程序的一些逻辑行为是否正确
基准函数BenchmarkFoo(b *testing.B)测试函数的性能
主测函数TestMain(m *testing.M)引导一个单元测试

1.2 测试命令

命令格式:

go test [-c] [-i] [build flags] [packages] [flags for test binary]
  • -cpu: 设置测试最大 cpu 逻辑数(也就是GPM中P, 最大并行执行的 gorouting 数量, 默认等于cpu核数)
  • -count:设置执行测试函数的次数, 默认为 1
  • -run: 执行功能测试函数, 支持正则匹配, 可以选择测试函数或者测试文件来仅测试单个函数或者单个文件
  • -bench:执行基准测试函数, 支持正在匹配
  • -benchtime:基准测试最大探索时间上限
  • -parallel:设置同一个被测试代码包中的功能测试函数的最大并发执行数
  • -v: 是展示测试过程信

二. 示例函数

示例函数,在Goland等IDE中调用该目标函数时,会智能提示Example示例哦👍。

// main.go
// Fib 斐波那契数「递归」,模拟耗时运算
func Fib(n int) int {
	if n < 2 {
		return n
	}
	return Fib(n-1) + Fib(n-2)
}
// main_test.go
// Example+函数名
func ExampleFib() {
	fmt.Println(Fib(10)) // 0.00s
	fmt.Println(Fib(20)) // 0.00s
	fmt.Println(Fib(30)) // 0.01s
	fmt.Println(Fib(40)) // 0.52s
	fmt.Println(Fib(42)) // 1.29s
	fmt.Println(Fib(43)) // 2.08s

	// Output:
	// 55
	// 6765
	// 832040
	// 102334155
	// 267914296
	// 433494437
}

在Fib源函数或被调实例上提示出了Example使用示例 😊😊😊~~~
在这里插入图片描述

三. 单元测试

单元测试(unit testing)是指对软件中的最小可测试单元进行检查和验证。

3.1 简单测试

// main.to 
// Add 加法运算
//go:generate go test -v -run='TestAdd'
func Add(a, b int) int {
	return a + b
}
//go:generate go test					      # 测试当前项目
//go:generate go test main.go main_test.go	  # 测试Go文件(须包含「源文件」和「测试文件」)
//go:generate go test -run='Add'			  # 执行测试函数(TestAdd*)
//go:generate go test -v -run='Add'			  # 始终显示结果
func TestAdd(t *testing.T) {
	actual, expect := Add(2, 1), 3
	if actual != expect {
		t.Errorf("测试失败:预期:%v,实际:%v", expect, actual)
		return
	}
}

3.2 表格驱动

//go:generate go test -v -run='Add'
func TestAdd(t *testing.T) {
	type args struct {
		a int
		b int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		{args: args{a: 1, b: 2}, want: 3},
		{args: args{a: 1, b: 0}, want: 1},
		{args: args{a: -1, b: -2}, want: -3},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Add(tt.args.a, tt.args.b);   got != tt.want {
				t.Errorf("结果: %v, 期望: %v", got, tt.want)
			}
		})
	}
}

3.3 测试覆盖率

执行go test --cover查看测试覆盖率

// main.go
func Score(score int) string {
	switch {
	case score >= 90: // 优秀
		return "A"
	case score >= 80: // 良好
		return "B"
	case score >= 60: // 及格
		return "C"
	default: // 不及格
		return "D"
	}
}
// main_test.go
//go:generate go test --cover					# 项目测试覆盖率
//go:generate go test --cover -run='Score'		# Score测试函数在项目中的测试覆盖率
func TestScore(t *testing.T) {
	type args struct{ a int }
	tests := []struct {
		name string
		args args
		want string
	}{
		// TODO: 添加测试用例
		{name: "测试优秀", args: args{a: 98}, want: "A"},
		{name: "测试良好",args: args{a: 88}, want: "B"},
		{args: args{a: 80}, want: "B"},
		{args: args{a: 70}, want: "C"},
		{args: args{a: 55}, want: "D"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Score(tt.args.a); got != tt.want {
				t.Errorf("结果: %v, 期望: %v", got, tt.want)
			}
		})
	}
}

查看覆盖率报告:

可将测试结果导出为测试报告,并使用cover可视化工具查看报告:

$ go test --cover -run='WithCover' -coverprofile=cover.out
$ go tool cover -html=cover.out

在这里插入图片描述

3.4 子测试

表格驱动中就是使用的子测试,子测试在对比测试中很有用,如:比对「递归斐波那契数列」耗时:

// 子测试(可忽略name)
//go:generate go test -run='Fib' -v
func TestFib(t *testing.T) {
	t.Run("参数10", func(t *testing.T) { Fib(10) })
	t.Run("参数20", func(t *testing.T) { Fib(20) })
	t.Run("", func(t *testing.T) { Fib(30) })
	t.Run("", func(t *testing.T) { Fib(40) })
	t.Run("", func(t *testing.T) { Fib(42) })
	t.Run("", func(t *testing.T) { Fib(43) })
}
$ go test -run='Fib' -v
=== RUN   TestFib
=== RUN   TestFib/参数10
=== RUN   TestFib/参数20
=== RUN   TestFib/#00
=== RUN   TestFib/#01
=== RUN   TestFib/#02
=== RUN   TestFib/#03
--- PASS: TestFib (4.04s)
    --- PASS: TestFib/参数10 (0.00s)
    --- PASS: TestFib/参数20 (0.00s)
    --- PASS: TestFib/#00 (0.00s)
    --- PASS: TestFib/#01 (0.52s)
    --- PASS: TestFib/#02 (1.33s)
    --- PASS: TestFib/#03 (2.18s)
PASS
ok      mytest  4.043s

四. 基准测试

基准测试性能测试,用以获得代码内存占用和运行效率的性能数据。

4.1 测试性能

命令格式: go test -bench=. [-parallel=1] [-cpu=2]

// 测试生成UUID (github.com/google/uuid)
func BenchmarkUUID(b *testing.B) {
	for i := 0; i < b.N; i++ {
		uuid.New()
	}
}
# 持续运行3秒,指定基准测试函数匹配规则,并忽略功能测试
$ go test -benchtime=3s -bench=UUID -run=none
goos: darwin
goarch: amd64
pkg: mytest
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkUUID-4          3902818               879.0 ns/op
PASS
ok      mytest  4.371s

共计运行3902818次,平均耗时879.0纳秒。

4.2 测试内存

命令格式: go test -benchmem -bench=<FuncName>

func BenchmarkAlloc(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fmt.Sprintf("%d", i)
	}
}
$ go test -benchmem -bench=Alloc -run=none
goos: darwin
goarch: amd64
pkg: mytest
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkAlloc-4        12507582               103.4 ns/op            16 B/op          1 allocs/op
PASS
ok      mytest  1.398s

共计运行12507582次,平均耗时103.4纳秒;每次调用需分配16字节,每次调用有1次分配。

4.2 测试并发

以协程并发的方式执行性能测试,可与常规性能测试结果比对性能差异

//go:generate go test -bench=FibParallel -run=none
func BenchmarkFibParallel(b *testing.B) {
    b.SetParallelism(1) // 设置使用的CPU数
   b.RunParallel(func(pb *testing.PB) {
      for pb.Next() {
          Fib(20)
      }
   })
}

4.4 控制计时器

有些测试需要一定的启动和初始化时间,如果从Benchmark()函数开始计时会很大程度上影响测试结果的精准性

//go:generate go test -bench=TimerControl -run=none
func BenchmarkTimerControl(b *testing.B) {
	<-time.After(time.Second) // 睡1秒
	b.ResetTimer()            // 重置计时器
	// b.StopTimer()  // 停止计时器
	// b.StartTimer() // 开始计时器

	var n int
	for i := 0; i < b.N; i++ {
		n++
	}
}

五. 主测函数

每次执行单元测试时,都会由TestMain发起,相当于测试函数的入口(main)函数

// m.Run()返回一个退出代码,以传递给os.Exit。
func TestMain(m *testing.M) {
	var (
		start  = "安装/启动测试..."
		finish = "卸载/完成测试..."
	)
	fmt.Println(start)
	code := m.Run() // 执行单元测试,并返回Code
	fmt.Println(finish)
	os.Exit(code) // 退出测试
}

六. pprof监控

6.1 测试实例A

测试fmt.Sprintf,strconv.FormatIntstrconv.Itoa的性能:

package main

import (
	"fmt"
	"strconv"
	"testing"
)

func BenchmarkStrconvSprintf(b *testing.B) {
	num := 10
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		fmt.Sprintf("%d", num)
	}
}

func BenchmarkStrconvFormat(b *testing.B) {
	num := int64(10)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		strconv.FormatInt(num, 10)
	}
}

func BenchmarkStrconvItoa(b *testing.B) {
	num := 10
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		strconv.Itoa(num)
	}
}
$ go test -bench=Strconv* -run=none
goos: darwin
goarch: amd64
pkg: mytest
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkStrconvSprintf-4       15038674                75.47 ns/op
BenchmarkStrconvFormat-4        442853013                2.495 ns/op
BenchmarkStrconvItoa-4          468989292                2.530 ns/op
PASS
ok      mytest  4.056s

6.2 测试实例B

测试以递归方式实现「斐波那契额数列」的函数性能:

package main

import (
	"testing"
)

func BenchmarkNormalFib05(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(5)
	}
}

func BenchmarkNormalFib10(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(10)
	}
}

func BenchmarkNormalFib20(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(20)
	}
}

func BenchmarkNormalFib30(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(30)
	}
}

func BenchmarkNormalFib40(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(40)
	}
}

func BenchmarkNormalFib42(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Fib(42)
	}
}
$ go test -bench=BenchmarkNormalFib* -run=none
goos: darwin
goarch: amd64
pkg: mytest
cpu: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
BenchmarkNormalFib05-4          46707842                22.01 ns/op
BenchmarkNormalFib10-4           4630387               262.4 ns/op
BenchmarkNormalFib20-4             37680             34472 ns/op
BenchmarkNormalFib30-4               298           3935120 ns/op
BenchmarkNormalFib40-4                 3         565669482 ns/op
BenchmarkNormalFib42-4                 1        1340423143 ns/op
PASS
ok      mytest  10.247s

6.3 查看性能报告

可将以上测试结果导出为测试报告,并使用pprof工具查看报告:

# 导出测试报告
$ go test -bench=Strconv* -run=none -cpuprofile cpu.out -memprofile mem.out
$ go test -bench=BenchmarkNormalFib* -run=none -cpuprofile cpu.out -memprofile mem.out
# 查看测试报告
$ go tool pprof -text mem.out

# 生成cpu.pdf和cpu.svg文件
go tool pprof -svg cpu.out > cpu.svg
go tool pprof -svg cpu.out > cpu.pdf

# 浏览器查看
$ go tool pprof -http=":8081" ./cpu.out
$ go tool pprof -http=":8081" ./mem.out

在这里插入图片描述

参考链接

https://studygolang.com/static/pkgdoc/pkg/testing.htm
https://github.com/google/pprof/blob/master/doc/README.md

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:23:19  更:2022-01-29 23:23:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 2:32:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码