测试和性能
Go 语言内置了强大的测试框架,支持单元测试、基准测试和性能分析。
单元测试
基本测试
测试文件以 _test.go 结尾,测试函数以 Test 开头:
go
// math.go
package math
func Add(a, b int) int {
return a + b
}
// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; 期望 5", result)
}
}运行测试
bash
# 运行当前包的所有测试
go test
# 运行特定测试
go test -run TestAdd
# 显示详细输出
go test -v
# 显示覆盖率
go test -cover表驱动测试
go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a int
b int
expected int
}{
{"正数", 2, 3, 5},
{"负数", -2, -3, -5},
{"零", 0, 5, 5},
{"混合", -2, 3, 1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; 期望 %d",
tt.a, tt.b, result, tt.expected)
}
})
}
}测试辅助函数
go
func assertEqual(t *testing.T, got, want int) {
t.Helper() // 标记为辅助函数
if got != want {
t.Errorf("得到 %d, 期望 %d", got, want)
}
}
func TestAdd(t *testing.T) {
assertEqual(t, Add(2, 3), 5)
}测试设置和清理
go
func setup() {
// 测试前设置
}
func teardown() {
// 测试后清理
}
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}基准测试
基准测试函数以 Benchmark 开头:
go
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}运行基准测试
bash
# 运行基准测试
go test -bench=.
# 运行特定基准测试
go test -bench=BenchmarkAdd
# 显示内存分配
go test -bench=. -benchmem
# 比较基准测试
go test -bench=. -benchmem > old.txt
# 修改代码后
go test -bench=. -benchmem > new.txt
benchcmp old.txt new.txt基准测试示例
go
func BenchmarkStringConcat(b *testing.B) {
var result string
for i := 0; i < b.N; i++ {
result += "a"
}
}
func BenchmarkStringBuilder(b *testing.B) {
var builder strings.Builder
for i := 0; i < b.N; i++ {
builder.WriteString("a")
}
_ = builder.String()
}示例测试
示例测试以 Example 开头,用于文档:
go
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}性能分析
CPU 性能分析
go
import (
"os"
"runtime/pprof"
)
func main() {
f, _ := os.Create("cpu.prof")
defer f.Close()
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 你的代码
}内存性能分析
go
import "runtime/pprof"
func main() {
f, _ := os.Create("mem.prof")
defer f.Close()
// 你的代码
pprof.WriteHeapProfile(f)
}使用 go test 进行性能分析
bash
# CPU 性能分析
go test -cpuprofile=cpu.prof -bench=.
# 内存性能分析
go test -memprofile=mem.prof -bench=.
# 查看性能分析结果
go tool pprof cpu.prof代码覆盖率
生成覆盖率报告
bash
# 生成覆盖率文件
go test -coverprofile=coverage.out
# 查看覆盖率
go tool cover -func=coverage.out
# 生成 HTML 报告
go tool cover -html=coverage.out覆盖率示例
go
func TestCoverage(t *testing.T) {
// 测试各种情况以提高覆盖率
Add(1, 2)
Add(-1, -2)
Add(0, 0)
}测试工具
testify
第三方测试库,提供更多断言功能:
go
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAdd(t *testing.T) {
result := Add(2, 3)
assert.Equal(t, 5, result)
require.NotNil(t, result)
}httptest
HTTP 测试工具:
go
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
handler(w, req)
if w.Code != http.StatusOK {
t.Errorf("期望状态码 %d, 得到 %d", http.StatusOK, w.Code)
}
}完整示例
go
// math.go
package math
func Add(a, b int) int {
return a + b
}
func Multiply(a, b int) int {
return a * b
}
// math_test.go
package math
import (
"testing"
)
func TestAdd(t *testing.T) {
tests := []struct {
name string
a int
b int
expected int
}{
{"正数", 2, 3, 5},
{"负数", -2, -3, -5},
{"零", 0, 5, 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; 期望 %d",
tt.a, tt.b, result, tt.expected)
}
})
}
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}最佳实践
- 测试应该快速:避免慢速测试影响开发效率
- 测试应该独立:每个测试不应该依赖其他测试
- 使用表驱动测试:提高测试覆盖率和可维护性
- 测试边界情况:包括正常情况、边界情况和错误情况
- 保持测试简单:测试代码应该易于理解和维护
- 定期运行基准测试:确保性能不会退化
- 关注代码覆盖率:但不要盲目追求 100% 覆盖率
下一步
接下来我们将学习:
