This commit is contained in:
2025-08-24 13:01:09 +08:00
parent 61e51ad014
commit f028913eb8
36 changed files with 10420 additions and 70 deletions

View File

@@ -0,0 +1,919 @@
/*
04-testing.go - Go 语言测试详解
学习目标:
1. 理解 Go 语言测试的基本概念
2. 掌握单元测试的编写方法
3. 学会基准测试和性能分析
4. 了解测试覆盖率和测试工具
5. 掌握测试的最佳实践
知识点:
- 测试函数的命名和签名
- testing 包的使用
- 表驱动测试模式
- 基准测试和内存分析
- 测试覆盖率
- 测试工具和技巧
*/
package main
import (
"fmt"
"sort"
"strings"
"time"
)
// 这个文件演示了如何编写各种类型的测试
// 注意:实际的测试文件应该以 _test.go 结尾
func main() {
fmt.Println("=== Go 语言测试详解 ===\n")
// 演示测试的基本概念
demonstrateTestingBasics()
// 演示单元测试
demonstrateUnitTesting()
// 演示表驱动测试
demonstrateTableDrivenTests()
// 演示基准测试
demonstrateBenchmarkTesting()
// 演示示例测试
demonstrateExampleTesting()
// 演示测试工具和技巧
demonstrateTestingTools()
// 演示测试的最佳实践
demonstrateTestingBestPractices()
}
// demonstrateTestingBasics 演示测试的基本概念
func demonstrateTestingBasics() {
fmt.Println("1. 测试的基本概念:")
// 测试的重要性
fmt.Printf(" 测试的重要性:\n")
fmt.Printf(" - 验证代码的正确性\n")
fmt.Printf(" - 防止回归错误\n")
fmt.Printf(" - 提高代码质量\n")
fmt.Printf(" - 便于重构和维护\n")
fmt.Printf(" - 作为代码的文档\n")
// Go 测试的特点
fmt.Printf(" Go 测试的特点:\n")
fmt.Printf(" - 内置测试框架\n")
fmt.Printf(" - 简单的测试语法\n")
fmt.Printf(" - 强大的工具支持\n")
fmt.Printf(" - 并行测试支持\n")
fmt.Printf(" - 基准测试集成\n")
// 测试文件的组织
fmt.Printf(" 测试文件的组织:\n")
fmt.Printf(" 源文件: calculator.go\n")
fmt.Printf(" 测试文件: calculator_test.go\n")
fmt.Printf(" \n")
fmt.Printf(" 源文件: utils.go\n")
fmt.Printf(" 测试文件: utils_test.go\n")
// 测试函数的类型
fmt.Printf(" 测试函数的类型:\n")
fmt.Printf(" 1. 单元测试: func TestXxx(*testing.T)\n")
fmt.Printf(" 2. 基准测试: func BenchmarkXxx(*testing.B)\n")
fmt.Printf(" 3. 示例测试: func ExampleXxx()\n")
fmt.Printf(" 4. 模糊测试: func FuzzXxx(*testing.F) (Go 1.18+)\n")
// 运行测试的命令
fmt.Printf(" 运行测试的命令:\n")
fmt.Printf(" go test # 运行当前包的测试\n")
fmt.Printf(" go test ./... # 运行所有子包的测试\n")
fmt.Printf(" go test -v # 详细输出\n")
fmt.Printf(" go test -run TestAdd # 运行特定测试\n")
fmt.Printf(" go test -bench=. # 运行基准测试\n")
fmt.Printf(" go test -cover # 显示覆盖率\n")
fmt.Println()
}
// demonstrateUnitTesting 演示单元测试
func demonstrateUnitTesting() {
fmt.Println("2. 单元测试:")
// 基本测试函数
fmt.Printf(" 基本测试函数:\n")
fmt.Printf(" func TestAdd(t *testing.T) {\n")
fmt.Printf(" result := Add(2, 3)\n")
fmt.Printf(" expected := 5\n")
fmt.Printf(" if result != expected {\n")
fmt.Printf(" t.Errorf(\\\"Add(2, 3) = %%d; want %%d\\\", result, expected)\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行测试
fmt.Printf(" 模拟运行测试:\n")
mockT := &MockT{}
// 测试 Add 函数
fmt.Printf(" 测试 Add 函数:\n")
TestAdd(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 测试 Divide 函数
fmt.Printf(" 测试 Divide 函数:\n")
mockT = &MockT{}
TestDivide(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 测试 IsPrime 函数
fmt.Printf(" 测试 IsPrime 函数:\n")
mockT = &MockT{}
TestIsPrime(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// testing.T 的方法
fmt.Printf(" testing.T 的方法:\n")
fmt.Printf(" t.Error(args ...) # 记录错误但继续测试\n")
fmt.Printf(" t.Errorf(format, args ...) # 格式化错误信息\n")
fmt.Printf(" t.Fatal(args ...) # 记录错误并停止测试\n")
fmt.Printf(" t.Fatalf(format, args ...) # 格式化致命错误\n")
fmt.Printf(" t.Log(args ...) # 记录日志信息\n")
fmt.Printf(" t.Logf(format, args ...) # 格式化日志信息\n")
fmt.Printf(" t.Skip(args ...) # 跳过测试\n")
fmt.Printf(" t.Helper() # 标记为辅助函数\n")
fmt.Println()
}
// demonstrateTableDrivenTests 演示表驱动测试
func demonstrateTableDrivenTests() {
fmt.Println("3. 表驱动测试:")
// 表驱动测试的优势
fmt.Printf(" 表驱动测试的优势:\n")
fmt.Printf(" - 减少重复代码\n")
fmt.Printf(" - 易于添加新的测试用例\n")
fmt.Printf(" - 提高测试的可维护性\n")
fmt.Printf(" - 清晰的测试数据结构\n")
// 表驱动测试示例
fmt.Printf(" 表驱动测试示例:\n")
fmt.Printf(" func TestAddTable(t *testing.T) {\n")
fmt.Printf(" tests := []struct {\n")
fmt.Printf(" name string\n")
fmt.Printf(" a, b int\n")
fmt.Printf(" expected int\n")
fmt.Printf(" }{\n")
fmt.Printf(" {\\\"positive\\\", 2, 3, 5},\n")
fmt.Printf(" {\\\"negative\\\", -1, -2, -3},\n")
fmt.Printf(" {\\\"zero\\\", 0, 5, 5},\n")
fmt.Printf(" }\n")
fmt.Printf(" \n")
fmt.Printf(" for _, tt := range tests {\n")
fmt.Printf(" t.Run(tt.name, func(t *testing.T) {\n")
fmt.Printf(" result := Add(tt.a, tt.b)\n")
fmt.Printf(" if result != tt.expected {\n")
fmt.Printf(" t.Errorf(\\\"got %%d, want %%d\\\", result, tt.expected)\n")
fmt.Printf(" }\n")
fmt.Printf(" })\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行表驱动测试
fmt.Printf(" 模拟运行表驱动测试:\n")
mockT := &MockT{}
TestAddTable(mockT)
// 字符串处理的表驱动测试
fmt.Printf(" 字符串处理的表驱动测试:\n")
mockT = &MockT{}
TestStringOperations(mockT)
// 错误处理的表驱动测试
fmt.Printf(" 错误处理的表驱动测试:\n")
mockT = &MockT{}
TestValidateInput(mockT)
fmt.Println()
}
// demonstrateBenchmarkTesting 演示基准测试
func demonstrateBenchmarkTesting() {
fmt.Println("4. 基准测试:")
// 基准测试的概念
fmt.Printf(" 基准测试的概念:\n")
fmt.Printf(" - 测量代码的性能\n")
fmt.Printf(" - 比较不同实现的效率\n")
fmt.Printf(" - 识别性能瓶颈\n")
fmt.Printf(" - 验证优化效果\n")
// 基准测试函数
fmt.Printf(" 基准测试函数:\n")
fmt.Printf(" func BenchmarkStringConcat(b *testing.B) {\n")
fmt.Printf(" for i := 0; i < b.N; i++ {\n")
fmt.Printf(" _ = \\\"hello\\\" + \\\"world\\\"\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟运行基准测试
fmt.Printf(" 模拟运行基准测试:\n")
// 字符串连接基准测试
fmt.Printf(" 字符串连接性能比较:\n")
runBenchmark("StringConcat", BenchmarkStringConcat)
runBenchmark("StringBuffer", BenchmarkStringBuffer)
runBenchmark("StringBuilder", BenchmarkStringBuilder)
// 排序算法基准测试
fmt.Printf(" 排序算法性能比较:\n")
runBenchmark("BubbleSort", BenchmarkBubbleSort)
runBenchmark("QuickSort", BenchmarkQuickSort)
runBenchmark("StandardSort", BenchmarkStandardSort)
// 基准测试的运行参数
fmt.Printf(" 基准测试的运行参数:\n")
fmt.Printf(" go test -bench=. # 运行所有基准测试\n")
fmt.Printf(" go test -bench=BenchmarkAdd # 运行特定基准测试\n")
fmt.Printf(" go test -bench=. -benchmem # 显示内存分配\n")
fmt.Printf(" go test -bench=. -benchtime=10s # 设置运行时间\n")
fmt.Printf(" go test -bench=. -count=5 # 运行多次\n")
// 基准测试结果解读
fmt.Printf(" 基准测试结果解读:\n")
fmt.Printf(" BenchmarkAdd-8 1000000000 0.50 ns/op 0 B/op 0 allocs/op\n")
fmt.Printf(" │ │ │ │ │\n")
fmt.Printf(" │ │ │ │ └─ 每次操作的分配次数\n")
fmt.Printf(" │ │ │ └─ 每次操作分配的字节数\n")
fmt.Printf(" │ │ └─ 每次操作的平均时间\n")
fmt.Printf(" │ └─ 执行次数\n")
fmt.Printf(" └─ 基准测试名称和CPU核心数\n")
fmt.Println()
}
// demonstrateExampleTesting 演示示例测试
func demonstrateExampleTesting() {
fmt.Println("5. 示例测试:")
// 示例测试的概念
fmt.Printf(" 示例测试的概念:\n")
fmt.Printf(" - 提供函数使用示例\n")
fmt.Printf(" - 验证输出结果\n")
fmt.Printf(" - 生成文档\n")
fmt.Printf(" - 确保示例代码正确\n")
// 示例测试函数
fmt.Printf(" 示例测试函数:\n")
fmt.Printf(" func ExampleAdd() {\n")
fmt.Printf(" result := Add(2, 3)\n")
fmt.Printf(" fmt.Println(result)\n")
fmt.Printf(" // Output: 5\n")
fmt.Printf(" }\n")
// 运行示例测试
fmt.Printf(" 运行示例测试:\n")
fmt.Printf(" Add 函数示例:\n")
ExampleAdd()
fmt.Printf(" Divide 函数示例:\n")
ExampleDivide()
fmt.Printf(" StringReverse 函数示例:\n")
ExampleStringReverse()
// 示例测试的特点
fmt.Printf(" 示例测试的特点:\n")
fmt.Printf(" 1. 函数名以 Example 开头\n")
fmt.Printf(" 2. 使用 // Output: 注释指定期望输出\n")
fmt.Printf(" 3. 可以使用 // Unordered output: 处理无序输出\n")
fmt.Printf(" 4. 会在 go doc 中显示\n")
fmt.Printf(" 5. 可以通过 go test 验证\n")
fmt.Println()
}
// demonstrateTestingTools 演示测试工具和技巧
func demonstrateTestingTools() {
fmt.Println("6. 测试工具和技巧:")
// 测试覆盖率
fmt.Printf(" 测试覆盖率:\n")
fmt.Printf(" go test -cover # 显示覆盖率\n")
fmt.Printf(" go test -coverprofile=cover.out # 生成覆盖率文件\n")
fmt.Printf(" go tool cover -html=cover.out # 生成HTML报告\n")
fmt.Printf(" go tool cover -func=cover.out # 显示函数覆盖率\n")
// 测试辅助函数
fmt.Printf(" 测试辅助函数:\n")
fmt.Printf(" func assertEqual(t *testing.T, got, want interface{}) {\n")
fmt.Printf(" t.Helper() // 标记为辅助函数\n")
fmt.Printf(" if got != want {\n")
fmt.Printf(" t.Errorf(\\\"got %%v, want %%v\\\", got, want)\n")
fmt.Printf(" }\n")
fmt.Printf(" }\n")
// 模拟使用辅助函数
fmt.Printf(" 使用辅助函数:\n")
mockT := &MockT{}
TestWithHelpers(mockT)
if mockT.failed {
fmt.Printf(" ❌ 测试失败: %s\n", mockT.errorMsg)
} else {
fmt.Printf(" ✅ 测试通过\n")
}
// 子测试
fmt.Printf(" 子测试:\n")
fmt.Printf(" t.Run(\\\"subtest\\\", func(t *testing.T) {\n")
fmt.Printf(" // 子测试代码\n")
fmt.Printf(" })\n")
// 并行测试
fmt.Printf(" 并行测试:\n")
fmt.Printf(" func TestParallel(t *testing.T) {\n")
fmt.Printf(" t.Parallel() // 标记为并行测试\n")
fmt.Printf(" // 测试代码\n")
fmt.Printf(" }\n")
// 测试设置和清理
fmt.Printf(" 测试设置和清理:\n")
fmt.Printf(" func TestMain(m *testing.M) {\n")
fmt.Printf(" setup() // 全局设置\n")
fmt.Printf(" code := m.Run()\n")
fmt.Printf(" teardown() // 全局清理\n")
fmt.Printf(" os.Exit(code)\n")
fmt.Printf(" }\n")
// 跳过测试
fmt.Printf(" 跳过测试:\n")
fmt.Printf(" if testing.Short() {\n")
fmt.Printf(" t.Skip(\\\"跳过长时间运行的测试\\\")\n")
fmt.Printf(" }\n")
// 临时目录
fmt.Printf(" 临时目录:\n")
fmt.Printf(" tmpDir := t.TempDir() // 创建临时目录\n")
fmt.Printf(" // 测试结束后自动清理\n")
fmt.Println()
}
// demonstrateTestingBestPractices 演示测试的最佳实践
func demonstrateTestingBestPractices() {
fmt.Println("7. 测试的最佳实践:")
// 测试命名
fmt.Printf(" 测试命名:\n")
fmt.Printf(" 1. 使用描述性的测试名称\n")
fmt.Printf(" 2. 包含被测试的功能和场景\n")
fmt.Printf(" 3. 使用一致的命名约定\n")
fmt.Printf(" \n")
fmt.Printf(" 好的命名:\n")
fmt.Printf(" TestAdd_PositiveNumbers\n")
fmt.Printf(" TestDivide_ByZero_ReturnsError\n")
fmt.Printf(" TestUser_Create_ValidInput\n")
// 测试组织
fmt.Printf(" 测试组织:\n")
fmt.Printf(" 1. 每个源文件对应一个测试文件\n")
fmt.Printf(" 2. 按功能分组测试\n")
fmt.Printf(" 3. 使用子测试组织复杂场景\n")
fmt.Printf(" 4. 保持测试的独立性\n")
// 测试数据
fmt.Printf(" 测试数据:\n")
fmt.Printf(" 1. 使用表驱动测试处理多个用例\n")
fmt.Printf(" 2. 包含边界条件和异常情况\n")
fmt.Printf(" 3. 使用有意义的测试数据\n")
fmt.Printf(" 4. 避免硬编码的魔法数字\n")
// 断言
fmt.Printf(" 断言:\n")
fmt.Printf(" 1. 使用清晰的错误消息\n")
fmt.Printf(" 2. 一个测试只验证一个行为\n")
fmt.Printf(" 3. 优先使用 Errorf 而不是 Error\n")
fmt.Printf(" 4. 在必要时使用 Fatal 停止测试\n")
// 测试覆盖率
fmt.Printf(" 测试覆盖率:\n")
fmt.Printf(" 1. 追求高覆盖率但不盲目追求100%%\n")
fmt.Printf(" 2. 关注重要的业务逻辑\n")
fmt.Printf(" 3. 测试边界条件和错误路径\n")
fmt.Printf(" 4. 使用覆盖率工具识别遗漏\n")
// 性能考虑
fmt.Printf(" 性能考虑:\n")
fmt.Printf(" 1. 避免在测试中进行复杂计算\n")
fmt.Printf(" 2. 使用并行测试提高速度\n")
fmt.Printf(" 3. 合理使用测试缓存\n")
fmt.Printf(" 4. 避免不必要的外部依赖\n")
// 测试维护
fmt.Printf(" 测试维护:\n")
fmt.Printf(" 1. 保持测试代码的简洁\n")
fmt.Printf(" 2. 定期重构测试代码\n")
fmt.Printf(" 3. 及时更新过时的测试\n")
fmt.Printf(" 4. 删除无用的测试\n")
// 常见陷阱
fmt.Printf(" 常见陷阱:\n")
fmt.Printf(" 1. 测试依赖外部状态\n")
fmt.Printf(" 2. 测试之间相互依赖\n")
fmt.Printf(" 3. 忽略错误处理的测试\n")
fmt.Printf(" 4. 过度复杂的测试逻辑\n")
fmt.Printf(" 5. 不稳定的测试flaky tests\n")
fmt.Println()
}
// ========== 被测试的函数 ==========
// Add 计算两个整数的和
func Add(a, b int) int {
return a + b
}
// Divide 计算两个数的商
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// IsPrime 判断是否为质数
func IsPrime(n int) bool {
if n < 2 {
return false
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
// StringReverse 反转字符串
func StringReverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Capitalize 将字符串首字母大写
func Capitalize(s string) string {
if len(s) == 0 {
return s
}
return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
}
// ValidateInput 验证输入
func ValidateInput(input string) error {
if len(input) == 0 {
return fmt.Errorf("input cannot be empty")
}
if len(input) > 100 {
return fmt.Errorf("input too long")
}
return nil
}
// ========== 模拟的 testing.T 和 testing.B ==========
// MockT 模拟 testing.T 类型
type MockT struct {
failed bool
errorMsg string
logs []string
}
func (m *MockT) Error(args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprint(args...)
}
func (m *MockT) Errorf(format string, args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprintf(format, args...)
}
func (m *MockT) Fatal(args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprint(args...)
}
func (m *MockT) Fatalf(format string, args ...interface{}) {
m.failed = true
m.errorMsg = fmt.Sprintf(format, args...)
}
func (m *MockT) Log(args ...interface{}) {
m.logs = append(m.logs, fmt.Sprint(args...))
}
func (m *MockT) Logf(format string, args ...interface{}) {
m.logs = append(m.logs, fmt.Sprintf(format, args...))
}
func (m *MockT) Helper() {
// 标记为辅助函数
}
func (m *MockT) Run(name string, f func(*MockT)) bool {
fmt.Printf(" 运行子测试: %s\n", name)
subT := &MockT{}
f(subT)
if subT.failed {
fmt.Printf(" ❌ 失败: %s\n", subT.errorMsg)
return false
} else {
fmt.Printf(" ✅ 通过\n")
return true
}
}
// MockB 模拟 testing.B 类型
type MockB struct {
N int
}
func (b *MockB) ResetTimer() {}
func (b *MockB) ReportAllocs() {}
// ========== 测试函数示例 ==========
// TestAdd 测试 Add 函数
func TestAdd(t *MockT) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
// TestDivide 测试 Divide 函数
func TestDivide(t *MockT) {
// 正常情况
result, err := Divide(10, 2)
if err != nil {
t.Errorf("Divide(10, 2) returned error: %v", err)
return
}
if result != 5 {
t.Errorf("Divide(10, 2) = %f; want 5", result)
}
// 除零情况
_, err = Divide(10, 0)
if err == nil {
t.Error("Divide(10, 0) should return error")
}
}
// TestIsPrime 测试 IsPrime 函数
func TestIsPrime(t *MockT) {
tests := []struct {
n int
expected bool
}{
{2, true},
{3, true},
{4, false},
{17, true},
{25, false},
{1, false},
{0, false},
{-1, false},
}
for _, tt := range tests {
result := IsPrime(tt.n)
if result != tt.expected {
t.Errorf("IsPrime(%d) = %t; want %t", tt.n, result, tt.expected)
}
}
}
// TestAddTable 表驱动测试示例
func TestAddTable(t *MockT) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"mixed numbers", -1, 2, 1},
{"zero", 0, 5, 5},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
result := Add(tt.a, tt.b)
if result != tt.expected {
subT.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
// TestStringOperations 字符串操作测试
func TestStringOperations(t *MockT) {
tests := []struct {
name string
input string
expected string
}{
{"normal string", "hello", "Hello"},
{"empty string", "", ""},
{"single char", "a", "A"},
{"already capitalized", "Hello", "Hello"},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
result := Capitalize(tt.input)
if result != tt.expected {
subT.Errorf("Capitalize(%q) = %q; want %q", tt.input, result, tt.expected)
}
})
}
}
// TestValidateInput 输入验证测试
func TestValidateInput(t *MockT) {
tests := []struct {
name string
input string
wantError bool
}{
{"valid input", "hello", false},
{"empty input", "", true},
{"too long input", strings.Repeat("a", 101), true},
{"max length input", strings.Repeat("a", 100), false},
}
for _, tt := range tests {
t.Run(tt.name, func(subT *MockT) {
err := ValidateInput(tt.input)
if (err != nil) != tt.wantError {
subT.Errorf("ValidateInput(%q) error = %v; wantError %t", tt.input, err, tt.wantError)
}
})
}
}
// TestWithHelpers 使用辅助函数的测试
func TestWithHelpers(t *MockT) {
assertEqual := func(t *MockT, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
// 使用辅助函数进行断言
assertEqual(t, Add(2, 3), 5)
assertEqual(t, Capitalize("hello"), "Hello")
result, err := Divide(10, 2)
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
assertEqual(t, result, 5.0)
}
// ========== 基准测试函数 ==========
// BenchmarkStringConcat 字符串连接基准测试
func BenchmarkStringConcat(b *MockB) {
for i := 0; i < b.N; i++ {
_ = "hello" + "world" + "golang" + "benchmark"
}
}
// BenchmarkStringBuffer 字符串缓冲区基准测试
func BenchmarkStringBuffer(b *MockB) {
for i := 0; i < b.N; i++ {
var buffer strings.Builder
buffer.WriteString("hello")
buffer.WriteString("world")
buffer.WriteString("golang")
buffer.WriteString("benchmark")
_ = buffer.String()
}
}
// BenchmarkStringBuilder 字符串构建器基准测试
func BenchmarkStringBuilder(b *MockB) {
for i := 0; i < b.N; i++ {
parts := []string{"hello", "world", "golang", "benchmark"}
_ = strings.Join(parts, "")
}
}
// BenchmarkBubbleSort 冒泡排序基准测试
func BenchmarkBubbleSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
bubbleSort(append([]int(nil), data...))
}
}
// BenchmarkQuickSort 快速排序基准测试
func BenchmarkQuickSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
quickSort(append([]int(nil), data...))
}
}
// BenchmarkStandardSort 标准排序基准测试
func BenchmarkStandardSort(b *MockB) {
data := []int{64, 34, 25, 12, 22, 11, 90}
for i := 0; i < b.N; i++ {
sort.Ints(append([]int(nil), data...))
}
}
// ========== 示例测试函数 ==========
// ExampleAdd Add 函数的示例
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}
// ExampleDivide Divide 函数的示例
func ExampleDivide() {
result, err := Divide(10, 2)
if err != nil {
fmt.Printf("Error: %v", err)
return
}
fmt.Printf("%.1f", result)
// Output: 5.0
}
// ExampleStringReverse StringReverse 函数的示例
func ExampleStringReverse() {
result := StringReverse("hello")
fmt.Println(result)
// Output: olleh
}
// ========== 辅助函数 ==========
// runBenchmark 运行基准测试
func runBenchmark(name string, benchFunc func(*MockB)) {
b := &MockB{N: 1000000}
start := time.Now()
benchFunc(b)
duration := time.Since(start)
nsPerOp := duration.Nanoseconds() / int64(b.N)
fmt.Printf(" %s: %d ns/op\n", name, nsPerOp)
}
// bubbleSort 冒泡排序
func bubbleSort(arr []int) {
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
// quickSort 快速排序
func quickSort(arr []int) {
if len(arr) < 2 {
return
}
pivot := partition(arr)
quickSort(arr[:pivot])
quickSort(arr[pivot+1:])
}
// partition 分区函数
func partition(arr []int) int {
pivot := arr[len(arr)-1]
i := -1
for j := 0; j < len(arr)-1; j++ {
if arr[j] <= pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[len(arr)-1] = arr[len(arr)-1], arr[i+1]
return i + 1
}
/*
运行这个程序:
go run 04-testing.go
实际测试文件示例calculator_test.go
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"mixed numbers", -1, 2, 1},
{"zero", 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("got %d, want %d", result, tt.expected)
}
})
}
}
func BenchmarkStringConcat(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = "hello" + "world"
}
}
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}
运行测试命令:
go test # 运行当前包的所有测试
go test -v # 详细输出
go test -run TestAdd # 运行特定测试
go test -bench=. # 运行基准测试
go test -cover # 显示覆盖率
go test ./... # 运行所有子包的测试
测试的核心概念:
1. 单元测试:验证单个函数或方法的正确性
2. 表驱动测试:使用测试数据表驱动测试执行
3. 基准测试:测量代码的性能和内存使用
4. 示例测试:提供函数使用示例并验证输出
测试的最佳实践:
1. 使用描述性的测试名称
2. 保持测试的独立性
3. 测试边界条件和错误情况
4. 使用表驱动测试处理多个用例
5. 编写清晰的错误消息
6. 保持高的测试覆盖率
7. 定期重构和维护测试代码
注意事项:
1. 测试文件必须以 _test.go 结尾
2. 测试函数必须以 Test 开头
3. 基准测试函数必须以 Benchmark 开头
4. 示例测试函数必须以 Example 开头
5. 使用 t.Helper() 标记辅助函数
6. 合理使用并行测试提高效率
*/