引言:为什么在 Go 中进行测试是必要的?
\nGo (Golang) 是一种用于构建可靠且可扩展系统的现代编程语言。然而,即使是最优雅的代码也无法避免错误。因此,测试是 Go 开发中不可或缺的一部分。内置的 testing 包为编写单元测试、集成测试甚至端到端 (e2e) 检查提供了强大的工具。
在本文中,我们将介绍 Go 中所有级别的测试:从简单的单元测试到复杂的端到端场景。您将学习如何正确组织测试、使用 mock、编写表驱动测试以及使用真实依赖项配置集成测试。本材料对新手和有经验的开发者都很有用,有助于系统化知识。
\n\n1. Go 中的单元测试:基础中的基础
\n单元测试在隔离外部依赖的情况下检查单个函数或方法的工作。在 Go 中,使用标准包 testing 来实现。基本规则:测试文件应命名为 *_test.go,测试函数应以 Test 开头。
1.1. 简单的单元测试
\n考虑一个两个数相加的函数:
\n// math.go\npackage math\n\nfunc Add(a, b int) int {\n return a + b\n}\n它的测试:
\n// math_test.go\npackage math\n\nimport "testing"\n\nfunc TestAdd(t *testing.T) {\n result := Add(2, 3)\n expected := 5\n if result != expected {\n t.Errorf("Add(2, 3) = %d; want %d", result, expected)\n }\n}\n运行测试:go test ./... 或 go test -v 以获取详细输出。
1.2. 表驱动测试
\nGo 社区广泛使用表驱动方法来测试多个用例:
\nfunc TestAddTableDriven(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n want int\n }{\n {"positive", 2, 3, 5},\n {"negative", -1, -2, -3},\n {"zero", 0, 0, 0},\n }\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n if got := Add(tt.a, tt.b); got != tt.want {\n t.Errorf("Add() = %d, want %d", got, tt.want)\n }\n })\n }\n}\n这种方法使测试易于阅读和扩展。
\n\n1.3. 错误测试
\n对于返回错误的函数,使用 t.Fatal 或 t.Errorf:
func TestDivide(t *testing.T) {\n _, err := Divide(10, 0)\n if err == nil {\n t.Fatal("expected error for division by zero")\n }\n}\n\n2. 集成测试:处理依赖项
\n集成测试检查组件之间的交互:数据库、API、文件系统。在 Go 中,通常使用构建标签或单独的包结构将它们与单元测试分开。
\n\n2.1. 使用构建标签
\n在文件开头使用标签 //go:build integration 创建文件:
//go:build integration\n\npackage db\n\nimport (\n "testing"\n "database/sql"\n _ "github.com/lib/pq"\n)\n\nfunc TestPostgresConnection(t *testing.T) {\n dsn := "host=localhost port=5432 user=test password=test dbname=test sslmode=disable"\n db, err := sql.Open("postgres", dsn)\n if err != nil {\n t.Fatalf("failed to open db: %v", err)\n }\n defer db.Close()\n \n if err := db.Ping(); err != nil {\n t.Fatalf("failed to ping db: %v", err)\n }\n}\n仅运行集成测试:go test -tags=integration ./...
2.2. Mock 和桩
\n为了隔离测试,使用接口和 mock。以流行的库 gomock 为例:
// user.go\ntype UserRepository interface {\n GetUser(id int) (*Us