بنقرة واحدة
golang-testing
保证测试驱动开发与 Go 代码高质量的全面测试策略。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
保证测试驱动开发与 Go 代码高质量的全面测试策略。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
生产级 API 的 REST API 设计模式,包括资源命名、状态码、分页、过滤、错误响应、版本控制和速率限制。
撰写文章、指南、博客、教程、时事通讯等长篇内容,并根据提供的示例或品牌指南呈现独特的语言风格。当用户需要篇幅超过一段的精炼文字,且重视风格一致性、结构和可信度时,请使用此技能。
后端架构模式、API 设计、数据库优化以及适用于 Node.js、Express 和 Next.js API 路由的服务端最佳实践。
适用于 TypeScript、JavaScript、React 和 Node.js 开发的通用编码标准、最佳实践与模式。
为 X、LinkedIn、TikTok、YouTube、时事通讯 (Newsletters) 以及多平台复用活动创建平台原生内容系统。当用户需要社交帖子、推文串 (Threads)、脚本、内容日历或将单一源素材清晰地适配到多个平台时使用。
Playwright E2E 测试模式、页面对象模型(POM)、配置、CI/CD 集成、产物管理以及不稳定测试(flaky test)策略。
| name | golang-testing |
| description | 保证测试驱动开发与 Go 代码高质量的全面测试策略。 |
保证测试驱动开发(TDD)与 Go 代码高质量的全面测试策略。
遵循编写失败测试、实现代码、重构(Refactor)的循环。
// 1. 编写测试(失败)
func TestCalculateTotal(t *testing.T) {
total := CalculateTotal([]float64{10.0, 20.0, 30.0})
want := 60.0
if total != want {
t.Errorf("got %f, want %f", total, want)
}
}
// 2. 实现代码(使测试通过)
func CalculateTotal(prices []float64) float64 {
var total float64
for _, price := range prices {
total += price
}
return total
}
// 3. 重构
// 在不破坏测试的前提下改进代码
体系化地测试多个用例。
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -2, -3, -5},
{"mixed signs", -2, 3, 1},
{"zeros", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.want {
t.Errorf("Add(%d, %d) = %d; want %d",
tt.a, tt.b, got, tt.want)
}
})
}
}
使用子测试组织逻辑测试。
func TestUser(t *testing.T) {
t.Run("validation", func(t *testing.T) {
t.Run("empty email", func(t *testing.T) {
user := User{Email: ""}
if err := user.Validate(); err == nil {
t.Error("expected validation error")
}
})
t.Run("valid email", func(t *testing.T) {
user := User{Email: "test@example.com"}
if err := user.Validate(); err != nil {
t.Errorf("unexpected error: %v", err)
}
})
})
t.Run("serialization", func(t *testing.T) {
// 另一个测试组
})
}
mypackage/
├── user.go
├── user_test.go # 单元测试
├── integration_test.go # 集成测试
├── testdata/ # 测试固件 (Test Fixtures)
│ ├── valid_user.json
│ └── invalid_user.json
└── export_test.go # 为内部测试提供的私有导出
// user_test.go - 同一包内(白盒测试)
package user
func TestInternalFunction(t *testing.T) {
// 可以测试内部逻辑
}
// user_external_test.go - 外部包(黑盒测试)
package user_test
import "myapp/user"
func TestPublicAPI(t *testing.T) {
// 仅测试公开 API
}
func TestBasicAssertions(t *testing.T) {
// 相等性
got := Calculate()
want := 42
if got != want {
t.Errorf("got %d, want %d", got, want)
}
// 错误检查
_, err := Process()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// nil 检查
result := GetResult()
if result == nil {
t.Fatal("expected non-nil result")
}
}
// 标记为 Helper(不会在堆栈跟踪中显示)
func assertEqual(t *testing.T, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
// 使用示例
func TestWithHelpers(t *testing.T) {
result, err := Process()
assertNoError(t, err)
assertEqual(t, result.Status, "success")
}
import "reflect"
func assertDeepEqual(t *testing.T, got, want interface{}) {
t.Helper()
if !reflect.DeepEqual(got, want) {
t.Errorf("got %+v, want %+v", got, want)
}
}
func TestStructEquality(t *testing.T) {
got := User{Name: "Alice", Age: 30}
want := User{Name: "Alice", Age: 30}
assertDeepEqual(t, got, want)
}
// 生产代码
type UserStore interface {
GetUser(id string) (*User, error)
SaveUser(user *User) error
}
type UserService struct {
store UserStore
}
// 测试代码
type MockUserStore struct {
users map[string]*User
err error
}
func (m *MockUserStore) GetUser(id string) (*User, error) {
if m.err != nil {
return nil, m.err
}
return m.users[id], nil
}
func (m *MockUserStore) SaveUser(user *User) error {
if m.err != nil {
return m.err
}
m.users[user.ID] = user
return nil
}
// 测试
func TestUserService(t *testing.T) {
mock := &MockUserStore{
users: make(map[string]*User),
}
service := &UserService{store: mock}
// 测试服务...
}
// 生产代码 - 使时间可注入
type TimeProvider interface {
Now() time.Time
}
type RealTime struct{}
func (RealTime) Now() time.Time {
return time.Now()
}
type Service struct {
time TimeProvider
}
// 测试代码
type MockTime struct {
current time.Time
}
func (m MockTime) Now() time.Time {
return m.current
}
func TestTimeDependent(t *testing.T) {
mockTime := MockTime{
current: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
service := &Service{time: mockTime}
// 在固定时间内测试...
}
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
type MockHTTPClient struct {
response *http.Response
err error
}
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
return m.response, m.err
}
func TestAPICall(t *testing.T) {
mockClient := &MockHTTPClient{
response: &http.Response{
StatusCode: 200,
Body: io.NopCloser(strings.NewReader(`{"status":"ok"}`)),
},
}
api := &APIClient{client: mockClient}
// 测试 API 客户端...
}
func TestHandler(t *testing.T) {
handler := http.HandlerFunc(MyHandler)
req := httptest.NewRequest("GET", "/users/123", nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
// 检查状态码
if rec.Code != http.StatusOK {
t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
}
// 检查响应体
var response map[string]interface{}
if err := json.NewDecoder(rec.Body).Decode(&response); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
if response["id"] != "123" {
t.Errorf("got id %v, want 123", response["id"])
}
}
func TestAuthMiddleware(t *testing.T) {
// 虚拟处理程序
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// 用中间件包装
handler := AuthMiddleware(nextHandler)
tests := []struct {
name string
token string
wantStatus int
}{
{"valid token", "valid-token", http.StatusOK},
{"invalid token", "invalid", http.StatusUnauthorized},
{"no token", "", http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
if tt.token != "" {
req.Header.Set("Authorization", "Bearer "+tt.token)
}
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != tt.wantStatus {
t.Errorf("got status %d, want %d", rec.Code, tt.wantStatus)
}
})
}
}
func TestAPIIntegration(t *testing.T) {
// 创建测试服务器
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]string{
"message": "hello",
})
}))
defer server.Close()
// 执行真实的 HTTP 请求
resp, err := http.Get(server.URL)
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
// 验证响应
var result map[string]string
json.NewDecoder(resp.Body).Decode(&result)
if result["message"] != "hello" {
t.Errorf("got %s, want hello", result["message"])
}
}
func TestUserRepository(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
tests := []struct {
name string
fn func(*testing.T, *sql.DB)
}{
{"create user", testCreateUser},
{"find user", testFindUser},
{"update user", testUpdateUser},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tx, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer tx.Rollback() // 测试后回滚
tt.fn(t, tx)
})
}
}
func setupTestDB(t *testing.T) *sql.DB {
t.Helper()
db, err := sql.Open("postgres", "postgres://localhost/test")
if err != nil {
t.Fatalf("failed to connect: %v", err)
}
// 迁移架构
if err := runMigrations(db); err != nil {
t.Fatalf("migrations failed: %v", err)
}
return db
}
func seedTestData(t *testing.T, db *sql.DB) {
t.Helper()
fixtures := []string{
`INSERT INTO users (id, email) VALUES ('1', 'test@example.com')`,
`INSERT INTO posts (id, user_id, title) VALUES ('1', '1', 'Test Post')`,
}
for _, query := range fixtures {
if _, err := db.Exec(query); err != nil {
t.Fatalf("failed to seed data: %v", err)
}
}
}
func BenchmarkCalculation(b *testing.B) {
for i := 0; i < b.N; i++ {
Calculate(100)
}
}
// 报告内存分配情况
func BenchmarkWithAllocs(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
ProcessData([]byte("test data"))
}
}
func BenchmarkEncoding(b *testing.B) {
data := generateTestData()
b.Run("json", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
json.Marshal(data)
}
})
b.Run("gob", func(b *testing.B) {
b.ReportAllocs()
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
enc.Encode(data)
buf.Reset()
}
})
}
// 运行: go test -bench=. -benchmem
func BenchmarkStringConcat(b *testing.B) {
b.Run("operator", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = "hello" + " " + "world"
}
})
b.Run("fmt.Sprintf", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("%s %s", "hello", "world")
}
})
b.Run("strings.Builder", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var sb strings.Builder
sb.WriteString("hello")
sb.WriteString(" ")
sb.WriteString("world")
_ = sb.String()
}
})
}
func FuzzParseInput(f *testing.F) {
// 种子语料库
f.Add("hello")
f.Add("world")
f.Add("123")
f.Fuzz(func(t *testing.T, input string) {
// 确保解析不会引发 panic
result, err := ParseInput(input)
// 确保即使有错误,结果不为 nil 或具有一致性
if err == nil && result == nil {
t.Error("got nil result with no error")
}
})
}
func FuzzJSONParsing(f *testing.F) {
f.Add([]byte(`{"name":"test","age":30}`))
f.Add([]byte(`{"name":"","age":0}`))
f.Fuzz(func(t *testing.T, data []byte) {
var user User
err := json.Unmarshal(data, &user)
// 如果 JSON 解码成功,理应可以再次编码
if err == nil {
_, err := json.Marshal(user)
if err != nil {
t.Errorf("marshal failed after successful unmarshal: %v", err)
}
}
})
}
# 运行测试并生成 HTML 报告
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# 显示每个包的覆盖率
go test -cover ./...
# 详细覆盖率
go test -coverprofile=coverage.out -covermode=atomic ./...
// Good: 易于测试的代码
func ProcessData(data []byte) (Result, error) {
if len(data) == 0 {
return Result{}, ErrEmptyData
}
// 每个分支均可测试
if isValid(data) {
return parseValid(data)
}
return parseInvalid(data)
}
// 对应的测试涵盖所有分支
func TestProcessData(t *testing.T) {
tests := []struct {
name string
data []byte
wantErr bool
}{
{"empty data", []byte{}, true},
{"valid data", []byte("valid"), false},
{"invalid data", []byte("invalid"), false},
}
// ...
}
//go:build integration
// +build integration
package myapp_test
import "testing"
func TestDatabaseIntegration(t *testing.T) {
// 需要真实数据库的测试
}
# 运行集成测试
go test -tags=integration ./...
# 排除集成测试
go test ./...
import "github.com/testcontainers/testcontainers-go"
func setupPostgres(t *testing.T) *sql.DB {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "postgres:15",
ExposedPorts: []string{"5432/tcp"},
Env: map[string]string{
"POSTGRES_PASSWORD": "test",
"POSTGRES_DB": "testdb",
},
}
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
container.Terminate(ctx)
})
// 连接到容器
// ...
return db
}
func TestParallel(t *testing.T) {
tests := []struct {
name string
fn func(*testing.T)
}{
{"test1", testCase1},
{"test2", testCase2},
{"test3", testCase3},
}
for _, tt := range tests {
tt := tt // 捕获循环变量
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // 并行执行此测试
tt.fn(t)
})
}
}
func TestWithResourceLimit(t *testing.T) {
// 同时仅运行 5 个测试
sem := make(chan struct{}, 5)
tests := generateManyTests()
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
sem <- struct{}{} // 获取
defer func() { <-sem }() // 释放
tt.fn(t)
})
}
}
# 基本测试
go test ./...
go test -v ./... # 详细输出
go test -run TestSpecific ./... # 运行特定测试
# 覆盖率
go test -cover ./...
go test -coverprofile=coverage.out ./...
# 竞态检测 (Race Condition)
go test -race ./...
# 基准测试
go test -bench=. ./...
go test -bench=. -benchmem ./...
go test -bench=. -cpuprofile=cpu.prof ./...
# 模糊测试
go test -fuzz=FuzzTest
# 集成测试
go test -tags=integration ./...
# JSON 格式(用于 CI 集成)
go test -json ./...
# 测试超时
go test -timeout 30s ./...
# 短时间测试(跳过耗时测试)
go test -short ./...
# 清除构建缓存
go clean -testcache
go test ./...
// Good: 通过表格驱动测试减少重复
func TestValidation(t *testing.T) {
tests := []struct {
input string
valid bool
}{
{"valid@email.com", true},
{"invalid-email", false},
{"", false},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
err := Validate(tt.input)
if (err == nil) != tt.valid {
t.Errorf("Validate(%q) error = %v, want valid = %v",
tt.input, err, tt.valid)
}
})
}
}
// Good: 将测试数据存放在 testdata/ 目录中
func TestLoadConfig(t *testing.T) {
data, err := os.ReadFile("testdata/config.json")
if err != nil {
t.Fatal(err)
}
config, err := ParseConfig(data)
// ...
}
func TestWithCleanup(t *testing.T) {
// 设置资源
file, err := os.CreateTemp("", "test")
if err != nil {
t.Fatal(err)
}
// 注册清理操作(类似于 defer,但在子测试中也有效)
t.Cleanup(func() {
os.Remove(file.Name())
})
// 继续测试...
}
// Bad: 错误信息不明确
if result != expected {
t.Error("wrong result")
}
// Good: 带有上下文的错误信息
if result != expected {
t.Errorf("Calculate(%d) = %d; want %d", input, result, expected)
}
// Better: 使用辅助函数
assertEqual(t, result, expected, "Calculate(%d)", input)
// Bad: 依赖外部状态
func TestBadDependency(t *testing.T) {
result := GetUserFromDatabase("123") // 使用真实数据库
// 测试脆弱且缓慢
}
// Good: 注入依赖
func TestGoodDependency(t *testing.T) {
mockDB := &MockDatabase{
users: map[string]User{"123": {ID: "123"}},
}
result := GetUser(mockDB, "123")
}
// Bad: 在测试间共享状态
var sharedCounter int
func TestShared1(t *testing.T) {
sharedCounter++
// 依赖测试顺序
}
// Good: 每个测试保持独立
func TestIndependent(t *testing.T) {
counter := 0
counter++
// 不影响其他测试
}
// Bad: 忽略错误
func TestIgnoreError(t *testing.T) {
result, _ := Process()
if result != expected {
t.Error("wrong result")
}
}
// Good: 检查错误
func TestCheckError(t *testing.T) {
result, err := Process()
if err != nil {
t.Fatalf("Process() error = %v", err)
}
if result != expected {
t.Errorf("got %v, want %v", result, expected)
}
}
| 命令/模式 | 目的 |
|---|---|
go test ./... | 运行所有测试 |
go test -v | 详细输出 |
go test -cover | 覆盖率报告 |
go test -race | 检测竞态条件 |
go test -bench=. | 运行基准测试 |
t.Run() | 子测试 |
t.Helper() | 测试辅助函数标识 |
t.Parallel() | 并行执行测试 |
t.Cleanup() | 注册清理操作 |
testdata/ | 测试固件目录 |
-short | 跳过耗时测试 |
-tags=integration | 通过构建标签运行测试 |
请记住:优秀的测试应该是快速、可靠、可维护且清晰的。追求清晰度而非复杂性。