Go 测试(模糊、基准、单元测试)

[toc]

单元测试

Go语言中自带轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试

单元测试能及时发现程序设计或者实现的逻辑错误,而性能测试能使程序在高并发的状态下还能保持稳定。

什么是可靠的性能基准测试环境

影响测试环境的软硬件因素:

  • 硬件:CPU型号、温度、IO等
  • 软件:操作系统版本、当前系统调度的负载等等

指导思想:

单次测试结构毫无意义,统计意义下 可对比的结果 是关键

  • 分析测试的场景、多次测试、决定统计检验的类型

可对比的结果是在可控的环境下得到的:

  • 笔记本电脑 CPU 的执行效率受电源管理等因素影响,连续测试同一段代码可能先得到短暂的性能提升,而后由于温度上升导致性能下降。
  • 虚拟机或者云服务器可能因为资源分配因素导致测量结果不稳定。

所以基准测试的两个基本目标

  • 可重复性:在其他外在条件不变的条件下,性能度量结果是稳定的、可重复性的
  • 可比较性:总是存在一个可以比较的基本线

测试用例的编写规则

测试规则:

  1. 测试用例文件必须以 _test 结尾,例如:util_test.go
  2. 功能测试用例方法必须 Test 开头
  3. 模糊测试用例方法必须 Fuzz 开头
  4. 基准测试用例方法必须 Benchmark 开头
  5. 文件夹_test 结尾的测试包,会被编译成分离包
  6. go help testgo help testflag 查看帮助

go test 模式

提示

我们可以使用在线环境进行测试,正好有一个在线的测试 k3s-runtime 我们使用它来构建。

1. 本地模式: go test或者 go test -v

2. 列表模式:编译并运行命令行上列出的每一个包,go test utilgo test ./

go-test 命令

注意:

go test 会编译所有 *_test.go 文件的包,这些文件可以包含 功能测试、banchmark测试、模糊测试。 所有以 _. 开头的文件都会被忽略,包括 _test.go

go test 前会运行 go vet 测试文件签名问题,如果在 go vert 过程中发现问题,将停止运行 go test

go test 的两种模式:

  • go test 编译运行当前文件测试文件。侧重模式下不 caching
  • go test .go test mathgo test ./...

go test 运行时,工作目录为被测试包所在的位置。

其他的一些参数

  • -benchtime t:指定测试时间,比如 1s 指定运行 1s
  • -count n : 指定运行多少次用例。如果 -cpu 指定了 cpu 数量,运行次数将会是 [n * cpu]
  • -cover:输出代码测试覆盖率
  • -json :以json方式输出测试报告
  • -failfast :一个测试用例失败后不去测试其他的测试用例
  • -run regexp:指定运行测试的规则
  • -cpu 1,2,4:指定 cpu 数量,会基于一个 cpu 跑一起,基于 2 个 cpu 跑一次,基于 4 个跑一次。对于benchmarks和模糊测试有意义。

测试用例

💡简单的一个案例如下:

package util

/* 测试用例,必须以util_test命名 */

import (
	"fmt"
	"testing"
)

var commTestData []commStruct

// 定义全局变量,用于测试
type commStruct struct {
	// 代码块
	Group    string
	SizeStr  string //输入
	Expected int64  //期望值
	Expected string //期望值
}

// 功能测试:主要验证功能是否正常
func TestParseSize(t *testing.T) {
	testData := commTestData
	for _, v := range testData {
		if v.Group == "ParseSize" {
			actual := ParseSize(v.SizeStr)
			if actual != v.Expected {
				t.Errorf("ParseSize(%s) = %d, expected %d", v.SizeStr, actual, v.Expected)
			}
		}
	}
}

// 模糊测试
func TestFuzzy(t *testing.T) {
	fmt.Println("util_test.go")
}

// 精确测试
func TestExact(t *testing.T) {
	fmt.Println("util_test.go")
}

// 性能测试
func Benchmark(b *testing.B) {
	fmt.Println("util_test.go")
}

// 基准测试
func Benchmark(b *testing.B) {
	fmt.Println("util_test.go")
}

// TestMain函数,用于测试前的初始化
func TestMain(m *testing.M) {
	initCommonData()
}

func initCommonData() {
	commTestData = []commStruct{ //测试数据
		{"ParseSize", "1", 1, "1"},
		{"ParseSize", "1k", 1024, "1k"},
		{"ParseSize", "1m", 1024 * 1024, "1m"},
		{"ParseSize", "1g", 1024 * 1024 * 1024, "1g"},
		{"ParseSize", "1t", 1024 * 1024 * 1024 * 1024, "1t"},
		{"ParseSize", "1p", 1024 * 1024 * 1024 * 1024 * 1024, "1p"},
		{"ParseSize", "1e", 1024 * 1024 * 1024 * 1024 * 1024 * 1024, "1e"},
		{"ParseSize", "1z", 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, "1z"},
	}
	m.Run()
}

END 链接