玩转Golang:深度探索测试与基准测试实战

发表时间: 2024-06-11 11:44

什么是Testing和Benchmarking?

在Golang中,测试和基准测试是两个关键的组成部分。测试(Testing)用于验证代码的正确性,而基准测试(Benchmarking)则用于评估代码的性能。两者相辅相成,确保我们的代码不仅功能正确,而且运行高效。

测试(Testing)

在Golang中,测试代码通常放在与被测试代码相同的包中,并且文件名以 _test.go 结尾。测试函数以 Test 开头,并接受一个 *testing.T 类型的参数。

func Add(a, b int) int {	return a + b}func TestAdd(t *testing.T) {	result := Add(1, 2)	if result != 3 {		t.Errorf("Add(1, 2) failed; expected 3, got %d", result)	}}

在上面的代码中,我们定义了一个简单的加法函数 Add,并编写了相应的测试函数 TestAdd。如果 Add 函数返回的结果不符合预期,测试将会失败。



基准测试(Benchmarking)

基准测试用于测量代码的性能,同样放在 _test.go 文件中,函数名以 Benchmark 开头,并接受一个 *testing.B 类型的参数。

func BenchmarkAdd(b *testing.B) {    for i := 0; i < b.N; i++ {        Add(1, 2)    }}

在这个基准测试中,我们反复调用 Add 函数 b.N 次,以测量其性能。b.N 是基准测试运行的迭代次数,由测试框架自动调整以获得稳定的测试结果。

goos: windowsgoarch: amd64pkg: go-orm-learn/go/testcpu: 12th Gen Intel(R) Core(TM) i5-12400FBenchmarkAddBenchmarkAdd-12         978122655                1.177 ns/opPASS

表驱动测试

在Golang中,表驱动测试是一种常见的测试模式。通过使用一个表格(slice)来存储测试用例,可以大大简化测试代码。

// 表驱动测试func TestAddTableDriven(t *testing.T) {	var tests = []struct {		a, b int		want int	}{		{1, 2, 3},		{0, 0, 0},		{-1, -1, -2},		{100, 100, 200},	}	for _, tt := range tests {		t.Run(fmt.Sprintf("Add(%d, %d)", tt.a, tt.b), func(t *testing.T) {			result := Add(tt.a, tt.b)			if result != tt.want {				t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.want)			}		})	}}

在这个例子中,我们定义了一个包含测试用例的切片,并通过 t.Run 运行每个测试用例。这样可以提高测试的可读性和维护性。运行的结果为:

=== RUN   TestAddTableDriven=== RUN   TestAddTableDriven/Add(1,_2)=== RUN   TestAddTableDriven/Add(0,_0)=== RUN   TestAddTableDriven/Add(-1,_-1)=== RUN   TestAddTableDriven/Add(100,_100)--- PASS: TestAddTableDriven (0.00s)    --- PASS: TestAddTableDriven/Add(1,_2) (0.00s)    --- PASS: TestAddTableDriven/Add(0,_0) (0.00s)    --- PASS: TestAddTableDriven/Add(-1,_-1) (0.00s)    --- PASS: TestAddTableDriven/Add(100,_100) (0.00s)PASS

子测试和子基准测试

有时候,我们需要对某些特定的子功能进行单独测试或基准测试。在Golang中,可以使用子测试和子基准测试来实现这一点。

func TestSub(t *testing.T) {	t.Run("group", func(t *testing.T) {		t.Run("Test1", func(t *testing.T) {			result := Add(1, 2)			if result != 3 {				t.Errorf("Add(1, 2) failed; expected 3, got %d", result)			}		})		t.Run("Test2", func(t *testing.T) {			result := Add(1, 2)			if result != 3 {				t.Errorf("Add(1, 2) failed; expected 3, got %d", result)			}		})	})}func BenchmarkSub(b *testing.B) {	b.Run("group", func(b *testing.B) {		b.Run("Benchmark1", func(b *testing.B) {			for i := 0; i < b.N; i++ {				Add(1, 2)			}		})		b.Run("Benchmark2", func(b *testing.B) {			for i := 0; i < b.N; i++ {				Add(1, 2)			}		})	})}

在这个例子中,我们使用 t.Run 和 b.Run 创建了子测试和子基准测试。这使得我们可以对不同的子功能进行独立测试和性能评估。

测试和基准测试的用法汇总

为了帮助大家更好地理解测试和基准测试的用法,下面我们用表格的形式来总结一下:

用法类型

示例代码

说明

基本测试

func TestAdd(t *testing.T) { /* 测试代码 */ }

编写基本的单元测试

基准测试

func BenchmarkAdd(b *testing.B) { /* 基准测试代码 */ }

编写基本的基准测试

表驱动测试

for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { /* 测试代码 */ }) }

使用表格驱动的方式编写测试

子测试

t.Run("SubTest", func(t *testing.T) { /* 子测试代码 */ })

编写子测试来测试特定子功能

子基准测试

b.Run("SubBenchmark", func(b *testing.B) { /* 子基准测试代码 */ })

编写子基准测试来评估特定子功能的性能

使用场景

那么,测试和基准测试到底有哪些实际的使用场景呢?下面列出了一些常见的应用场景,供大家参考:

  1. 单元测试:确保每个函数或方法的功能正确。
  2. 集成测试:验证多个模块协同工作的正确性。
  3. 性能优化:通过基准测试评估和优化代码性能。
  4. 回归测试:防止代码修改引入新的错误。
  5. 持续集成:自动运行测试和基准测试,确保代码质量。

希望大家通过这篇文章对Golang的测试和基准测试有一个全面的了解。在实际开发中,测试和基准测试是确保代码质量和性能的重要手段。希望大家能在项目中积极实践这些技术,不断提升代码的可靠性和效率。

如果你觉得这篇文章对你有所帮助,别忘了点赞、评论和关注哦!你的支持是我们不断创作优质内容的动力!如果你有任何疑问或建议,也欢迎在评论区留言,我们会及时回复。