Skip to content

calculator_test

文件信息

  • 📄 原文件:calculator_test.go
  • 🔤 语言:go

完整代码

go
package main

import (
	"fmt"
	"testing"
)

// ============================================================
//                      Go 测试基础
// ============================================================
// 测试文件命名: xxx_test.go
// 测试函数命名: TestXxx(必须以 Test 开头,后面首字母大写)
// 运行测试: go test [-v] [-run=pattern]

// ----------------------------------------------------------
// 基本测试
// ----------------------------------------------------------

// TestAbs 测试 Abs 函数
func TestAbs(t *testing.T) {
	// 测试正数
	result := Abs(5)
	if result != 5 {
		t.Errorf("Abs(5) = %d; want 5", result)
	}

	// 测试负数
	result = Abs(-5)
	if result != 5 {
		t.Errorf("Abs(-5) = %d; want 5", result)
	}

	// 测试零
	result = Abs(0)
	if result != 0 {
		t.Errorf("Abs(0) = %d; want 0", result)
	}
}

// TestMax 测试 Max 函数
func TestMax(t *testing.T) {
	if Max(3, 5) != 5 {
		t.Error("Max(3, 5) should be 5")
	}

	if Max(5, 3) != 5 {
		t.Error("Max(5, 3) should be 5")
	}

	if Max(3, 3) != 3 {
		t.Error("Max(3, 3) should be 3")
	}
}

// ----------------------------------------------------------
// 表格驱动测试(推荐方式)
// ----------------------------------------------------------

// TestFibonacci 使用表格驱动测试
func TestFibonacci(t *testing.T) {
	// 定义测试用例
	tests := []struct {
		name     string // 测试用例名称
		input    int    // 输入
		expected int    // 期望输出
	}{
		{"fib(0)", 0, 0},
		{"fib(1)", 1, 1},
		{"fib(2)", 2, 1},
		{"fib(5)", 5, 5},
		{"fib(10)", 10, 55},
	}

	// 遍历测试用例
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Fibonacci(tt.input)
			if result != tt.expected {
				t.Errorf("Fibonacci(%d) = %d; want %d", tt.input, result, tt.expected)
			}
		})
	}
}

// TestIsPrime 表格驱动测试
func TestIsPrime(t *testing.T) {
	tests := []struct {
		n        int
		expected bool
	}{
		{-1, false},
		{0, false},
		{1, false},
		{2, true},
		{3, true},
		{4, false},
		{5, true},
		{10, false},
		{17, true},
		{100, false},
	}

	for _, tt := range tests {
		// 使用 %d 作为子测试名称
		t.Run(string(rune(tt.n)), func(t *testing.T) {
			result := IsPrime(tt.n)
			if result != tt.expected {
				t.Errorf("IsPrime(%d) = %v; want %v", tt.n, result, tt.expected)
			}
		})
	}
}

// ----------------------------------------------------------
// 测试结构体方法
// ----------------------------------------------------------

func TestCalculator_Add(t *testing.T) {
	calc := NewCalculator()

	tests := []struct {
		a, b     float64
		expected float64
	}{
		{1, 2, 3},
		{-1, 1, 0},
		{0, 0, 0},
		{1.5, 2.5, 4},
	}

	for _, tt := range tests {
		result := calc.Add(tt.a, tt.b)
		if result != tt.expected {
			t.Errorf("Add(%v, %v) = %v; want %v", tt.a, tt.b, result, tt.expected)
		}
	}
}

func TestCalculator_Divide(t *testing.T) {
	calc := NewCalculator()

	// 正常除法
	result, err := calc.Divide(10, 2)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if result != 5 {
		t.Errorf("Divide(10, 2) = %v; want 5", result)
	}

	// 除以零
	_, err = calc.Divide(10, 0)
	if err == nil {
		t.Error("expected error for division by zero")
	}
}

// ----------------------------------------------------------
// 子测试
// ----------------------------------------------------------

func TestReverse(t *testing.T) {
	t.Run("ASCII", func(t *testing.T) {
		result := Reverse("hello")
		if result != "olleh" {
			t.Errorf("got %s; want olleh", result)
		}
	})

	t.Run("Unicode", func(t *testing.T) {
		result := Reverse("你好")
		if result != "好你" {
			t.Errorf("got %s; want 好你", result)
		}
	})

	t.Run("Empty", func(t *testing.T) {
		result := Reverse("")
		if result != "" {
			t.Errorf("got %s; want empty", result)
		}
	})
}

// ----------------------------------------------------------
// 跳过测试
// ----------------------------------------------------------

func TestSkipExample(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping in short mode")
	}
	// 耗时的测试...
	t.Log("Running full test")
}

// ----------------------------------------------------------
// 并行测试
// ----------------------------------------------------------

func TestParallel(t *testing.T) {
	t.Parallel() // 标记为可并行

	tests := []struct {
		name     string
		input    int
		expected int
	}{
		{"abs positive", 5, 5},
		{"abs negative", -5, 5},
		{"abs zero", 0, 0},
	}

	for _, tt := range tests {
		tt := tt // 捕获循环变量
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel() // 子测试也可以并行
			result := Abs(tt.input)
			if result != tt.expected {
				t.Errorf("Abs(%d) = %d; want %d", tt.input, result, tt.expected)
			}
		})
	}
}

// ----------------------------------------------------------
// Setup 和 Teardown
// ----------------------------------------------------------

func TestWithSetup(t *testing.T) {
	// Setup
	calc := NewCalculator()
	t.Log("Setup: Calculator created")

	// Cleanup(defer 确保执行)
	t.Cleanup(func() {
		t.Log("Cleanup: Resources released")
	})

	// 测试
	result := calc.Add(1, 2)
	if result != 3 {
		t.Errorf("Add(1, 2) = %v; want 3", result)
	}
}

// TestMain 可以用于全局 setup/teardown
// func TestMain(m *testing.M) {
//     // 全局 setup
//     fmt.Println("Global Setup")
//
//     code := m.Run() // 运行所有测试
//
//     // 全局 teardown
//     fmt.Println("Global Teardown")
//
//     os.Exit(code)
// }

// ----------------------------------------------------------
// 辅助函数
// ----------------------------------------------------------

// assertEqual 辅助断言函数
func assertEqual(t *testing.T, got, want interface{}) {
	t.Helper() // 标记为辅助函数,错误时报告调用者位置
	if got != want {
		t.Errorf("got %v; want %v", got, want)
	}
}

func TestWithHelper(t *testing.T) {
	assertEqual(t, Abs(-5), 5)
	assertEqual(t, Max(3, 5), 5)
}

// ============================================================
//                      基准测试 (Benchmark)
// ============================================================
// 函数命名: BenchmarkXxx
// 运行: go test -bench=. [-benchmem] [-benchtime=3s]

func BenchmarkFibonacci(b *testing.B) {
	// b.N 由测试框架自动调整
	for i := 0; i < b.N; i++ {
		Fibonacci(20)
	}
}

func BenchmarkFibonacciIterative(b *testing.B) {
	for i := 0; i < b.N; i++ {
		FibonacciIterative(20)
	}
}

// 基准测试子测试
func BenchmarkFibonacciComparison(b *testing.B) {
	b.Run("Recursive", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			Fibonacci(15)
		}
	})

	b.Run("Iterative", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			FibonacciIterative(15)
		}
	})
}

// 基准测试不同输入
func BenchmarkIsPrime(b *testing.B) {
	inputs := []int{17, 997, 7919}

	for _, n := range inputs {
		b.Run(string(rune(n)), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				IsPrime(n)
			}
		})
	}
}

// ============================================================
//                      示例函数 (Example)
// ============================================================
// 函数命名: ExampleXxx
// 用于文档和可运行示例
// Output: 注释标记期望输出

func ExampleGreet() {
	fmt.Println(Greet("World"))
	// Output: Hello, World!
}

func ExampleSum() {
	fmt.Println(Sum(1, 2, 3))
	fmt.Println(Sum(10, 20))
	// Output:
	// 6
	// 30
}

func ExampleReverse() {
	fmt.Println(Reverse("hello"))
	fmt.Println(Reverse("你好"))
	// Output:
	// olleh
	// 好你
}

// ============================================================
//                      重要注意事项
// ============================================================
//
// 1. 【测试命令】
//    go test              # 运行当前包测试
//    go test -v           # 详细输出
//    go test ./...        # 运行所有包测试
//    go test -run=Pattern # 运行匹配的测试
//
// 2. 【基准测试命令】
//    go test -bench=.           # 运行所有基准测试
//    go test -bench=. -benchmem # 显示内存分配
//    go test -bench=. -count=5  # 运行多次
//
// 3. 【覆盖率】
//    go test -cover                    # 显示覆盖率
//    go test -coverprofile=c.out       # 生成覆盖率文件
//    go tool cover -html=c.out         # HTML 报告
//
// 4. 【表格驱动测试】
//    推荐方式,易于维护和扩展
//    使用 t.Run 创建子测试
//
// 5. 【t.Helper()】
//    标记辅助函数,错误报告时显示调用者位置
//
// 6. 【t.Parallel()】
//    标记测试可并行运行
//    注意循环变量捕获
//
// 7. 【testing.Short()】
//    配合 -short 标志跳过耗时测试

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布