介绍:为什么在Go中进行测试是基础
Go (Golang) 的设计理念是简洁和可靠。内置的 testing 包是现代编程语言中编写测试的最佳工具之一。它不需要第三方库来处理基本场景,同时又易于扩展。在本文中,我们将讨论三个测试级别:unit、integration 和 e2e。您将学习如何编写它们、何时应用以及使用哪些工具。这些内容对新手和经验丰富的Go开发者都很有用。
1. Go中的单元测试:基础中的基础
单元测试在隔离环境中检查单个函数或方法。在Go中,只需创建一个后缀为 _test.go 的文件,并编写类似 TestXxx(t *testing.T) 的函数即可。
最简单的单元测试示例
假设我们有一个加法函数:
// math.gopackage main
func Add(a, b int) int { return a + b}它的测试代码:
// math_test.gopackage main
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) }}表格驱动测试 (Table-Driven Tests)
Go社区推荐使用表格驱动测试来检查多个用例:
func TestAddTable(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positive", 2, 3, 5}, {"negative", -1, -2, -3}, {"zero", 0, 0, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Add(tt.a, tt.b); got != tt.want { t.Errorf("Add() = %d, want %d", got, tt.want) } }) }}使用以下命令运行单元测试:
go test ./...2. 集成测试:检查交互
集成测试检查系统不同部分如何协同工作:数据库、API、文件系统。在Go中,通常将它们放在带有构建标签的单独文件中,或放在 integration 包中。
使用构建标签 (build tags)
创建一个带有标签的文件 integration_test.go:
//go:build integration
package integration
import ( "database/sql" "testing" _ "github.com/lib/pq")
func TestDatabaseConnection(t *testing.T) { dsn := "host=localhost port=5432 user=test dbname=test sslmode=disable" db, err := sql.Open("postgres", dsn) if err != nil { t.Fatalf("Failed to open DB: %v", err) } defer db.Close()
err = db.Ping() if err != nil { t.Fatalf("Failed to ping DB: %v", err) }}仅运行集成测试:
go test -tags=integration ./...使用测试数据库
对于集成测试,经常使用 testcontainers-go 或内存数据库(例如 SQLite)。使用 testcontainers 的示例:
func TestWithPostgresContainer(t *testing.T) { ctx := context.Background() pgContainer, err := postgres.RunContainer(ctx, testcontainers.WithImage("postgres:15"), postgres.WithDatabase("testdb"), ) require.NoError(t, err) defer pgContainer.Terminate(ctx)
dsn, _ := pgContainer.ConnectionString(ctx) // 使用 dsn 进行连接}3. E2E测试:全面检查系统
端到端测试模拟用户行为,并从开始到结束全面检查系统。在Go中,通常使用 net/http/httptest 包为HTTP服务编写此类测试。
REST API的E2E测试示例
func TestCreateUserE2E(t *testing.T) { // 准