Files
golang/golang-learning/09-advanced/04-testing.go
2025-08-24 13:01:09 +08:00

920 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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. 合理使用并行测试提高效率
*/