慣性聚合 高效追讀感興趣之博客、新聞、科技資訊
閱原文 以慣性聚合開啟

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
行单元测试:结构及最佳实践
Rost · 2026-05-24 · via DEV Community

Rost

Go之内置测试包
,供以简约之框架,无外倚,可作单元测试之要务。
兹列测试之根本,项目之结构,及高深之范式,以助构建可靠之Go应用。

Go中测试之要义

Go之理念,重简约而求可靠。标准库中含testing 之包,使单元测试为 Go 生态之翘楚。经严测之 Go 代码,增可维护,早捕谬误,且以例示为文。初涉 Go 者欲速览其要,可检视吾辈所撰 Go 速查表

Go 测试之要益:

  • 内置支持:无需外部框架
  • :执行迅捷:默认并行测试执行
  • :语法简明:样板代码极简
  • :工具有余:涵盖报告、基准测试及性能分析
  • :适配持续集成与持续部署:易于融入自动化流程

Go测试项目结构

Go测试与生产代码共存,命名规范明晰:

myproject/
├── go.mod
├── main.go
├── calculator.go
├── calculator_test.go
├── utils/
│   ├── helper.go
│   └── helper_test.go
└── models/
    ├── user.go
    └── user_test.go

全屏模式 退出全屏模式

主要规范:

  • 测试文件以_test.go
  • 结尾 _test测试与代码同包(或用__JHSNS_SEG_e6813b5e_26__后缀进行黑盒测试)
  • 每源文件,皆可有其对应的测试文件

软件包测试之法

白箱测试(同软件包):

package calculator

import "testing"
// Can access unexported functions and variables

进入全屏模式 退出全屏模式

黑箱测试(外部软件包):

package calculator_test

import (
    "testing"
    "myproject/calculator"
)
// Can only access exported functions (recommended for public APIs)

进入全屏模式 退出全屏模式

基本测试结构

每测试函数皆循此式:

package calculator

import "testing"

// Test function must start with "Test"
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) = %d; want %d", result, expected)
    }
}

入全屏模式 出全屏模式

Testing.T方法:

  • t.Error() / t.Errorf():标测试为败而续行
  • t.Fatal() / t.Fatalf():标测试为败而立止
  • t.Log() /t.Logf():日志输出(唯-v标志显之)
  • t.Skip() / t.Skipf():略试
  • t.Parallel():并行试诸试

t.Log乃为可读之试辨。运行之务,log/slog及JSON善容之记,常宜于聚汇与事弊之查。详见以slog在Go中实现结构化日志,用于可观测性与告警.

表驱动测试:Go之正道

表驱动测试乃Go中测试多场景之惯用之法。借Go泛型之力,亦可创制类型安全之测试助手,通行于异类数据之域:

func TestCalculate(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        op       string
        expected int
        wantErr  bool
    }{
        {"addition", 2, 3, "+", 5, false},
        {"subtraction", 5, 3, "-", 2, false},
        {"multiplication", 4, 3, "*", 12, false},
        {"division", 10, 2, "/", 5, false},
        {"division by zero", 10, 0, "/", 0, true},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result, err := Calculate(tt.a, tt.b, tt.op)

            if (err != nil) != tt.wantErr {
                t.Errorf("Calculate() error = %v, wantErr %v", err, tt.wantErr)
                return
            }

            if result != tt.expected {
                t.Errorf("Calculate(%d, %d, %q) = %d; want %d", 
                    tt.a, tt.b, tt.op, result, tt.expected)
            }
        })
    }
}

进入全屏模式 退出全屏模式

优势:

  • 一函数试多境
  • 添新试案易
  • 行文明晰待行
  • 试组井然可持

运试

基本命令

# Run tests in current directory
go test

# Run tests with verbose output
go test -v

# Run tests in all subdirectories
go test ./...

# Run specific test
go test -run TestAdd

# Run tests matching pattern
go test -run TestCalculate/addition

# Run tests in parallel (default is GOMAXPROCS)
go test -parallel 4

# Run tests with timeout
go test -timeout 30s

入全屏模式 退出全屏模式

测试覆盖率

# Run tests with coverage
go test -cover

# Generate coverage profile
go test -coverprofile=coverage.out

# View coverage in browser
go tool cover -html=coverage.out

# Show coverage by function
go tool cover -func=coverage.out

# Set coverage mode (set, count, atomic)
go test -covermode=count -coverprofile=coverage.out

进入全屏模式 退出全屏模式

有用标志

  • -short:运行标记if testing.Short()检查的测试
  • -race:启用竞争检测器(发现并发访问问题)
  • -cpu:指定GOMAXPROCS值
  • -count n:运行每个测试n次
  • -failfast:首测失则止

测试助手与设置/拆除

助手函数

t.Helper()标记助手函数,以增误报之明:

func assertEqual(t *testing.T, got, want int) {
    t.Helper() // This line is reported as the caller
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }
}

func TestMath(t *testing.T) {
    result := Add(2, 3)
    assertEqual(t, result, 5) // Error line points here
}

全屏模式入 全屏模式出

设置与拆除

func TestMain(m *testing.M) {
    // Setup code here
    setup()

    // Run tests
    code := m.Run()

    // Teardown code here
    teardown()

    os.Exit(code)
}

全屏模式入 全屏模式出

测试固件

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func TestSomething(t *testing.T) {
    teardown := setupTestCase(t)
    defer teardown(t)

    // Test code here
}

入全景模式 出全屏模式

嘲弄与依赖注入

基于接口的模拟

试验与数据库交互之码,用接口可轻易造模拟实现。若尔于 Go 中用 PostgreSQL,请参吾等比较Go ORM 择数据库之良库,须具可测之能.

// Production code
type Database interface {
    GetUser(id int) (*User, error)
}

type UserService struct {
    db Database
}

func (s *UserService) GetUserName(id int) (string, error) {
    user, err := s.db.GetUser(id)
    if err != nil {
        return "", err
    }
    return user.Name, nil
}

// Test code
type MockDatabase struct {
    users map[int]*User
}

func (m *MockDatabase) GetUser(id int) (*User, error) {
    if user, ok := m.users[id]; ok {
        return user, nil
    }
    return nil, errors.New("user not found")
}

func TestGetUserName(t *testing.T) {
    mockDB := &MockDatabase{
        users: map[int]*User{
            1: {ID: 1, Name: "Alice"},
        },
    }

    service := &UserService{db: mockDB}
    name, err := service.GetUserName(1)

    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if name != "Alice" {
        t.Errorf("got %s, want Alice", name)
    }
}

Enter fullscreen mode Exit fullscreen mode

测试之库,众所习用

Testify

Go言测试之库,尤善于断言与模拟者.

import (
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

func TestWithTestify(t *testing.T) {
    result := Add(2, 3)
    assert.Equal(t, 5, result, "they should be equal")
    assert.NotNil(t, result)
}

// Mock example
type MockDB struct {
    mock.Mock
}

func (m *MockDB) GetUser(id int) (*User, error) {
    args := m.Called(id)
    return args.Get(0).(*User), args.Error(1)
}

Enter fullscreen mode Exit fullscreen mode

他用之器

  • gomock:谷歌之模拟框架,代码生成也
  • httptest:测试HTTP处理器之标准库也
  • testcontainers-go:以Docker容器为集成测试也
  • ginkgo/gomega:行为驱动测试之框架也

試驗與外部服務如AI模型之整合,需虛擬或代換其依賴。譬如,若君以Ollama于Go,可思創介面之包覆,使君之碼更易試驗。

基準測試

Go含內建之基準測試支援:

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

// Run benchmarks
// go test -bench=. -benchmem

進入全屏模式 退出全屏模式

输出每秒迭代次数与内存分配

最佳实践

  1. 撰写表格驱动测试:为多个测试用例使用结构体切片模式
  2. 使用t.Run进行子测试:组织更优,可选择性运行子测试
  3. 先测试导出函数:专注公共接口之表现
  4. :测试务须简明:每试当验一事
  5. :测试名目须有义:述所试之实与所期之果
  6. :勿试施行之细:试其行,非其内
  7. :依界面以立依存:嘲弄易行
  8. 高覆盖率,然质重于量百不失一,非谓无瑕
  9. 以-race标志行测试早察竞态之患
  10. 以TestMain行繁重之设勿复设于每试

例:全试之套

package user

import (
    "errors"
    "testing"
)

type User struct {
    ID    int
    Name  string
    Email string
}

func ValidateUser(u *User) error {
    if u.Name == "" {
        return errors.New("name cannot be empty")
    }
    if u.Email == "" {
        return errors.New("email cannot be empty")
    }
    return nil
}

// Test file: user_test.go
func TestValidateUser(t *testing.T) {
    tests := []struct {
        name    string
        user    *User
        wantErr bool
        errMsg  string
    }{
        {
            name:    "valid user",
            user:    &User{ID: 1, Name: "Alice", Email: "alice@example.com"},
            wantErr: false,
        },
        {
            name:    "empty name",
            user:    &User{ID: 1, Name: "", Email: "alice@example.com"},
            wantErr: true,
            errMsg:  "name cannot be empty",
        },
        {
            name:    "empty email",
            user:    &User{ID: 1, Name: "Alice", Email: ""},
            wantErr: true,
            errMsg:  "email cannot be empty",
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := ValidateUser(tt.user)

            if (err != nil) != tt.wantErr {
                t.Errorf("ValidateUser() error = %v, wantErr %v", err, tt.wantErr)
                return
            }

            if err != nil && err.Error() != tt.errMsg {
                t.Errorf("ValidateUser() error message = %v, want %v", err.Error(), tt.errMsg)
            }
        })
    }
}

入全景模式 出全景模式

有用之链

结论

Go之测试框架,备齐万全,而设之简。循Go之常法,若驱表式之试,用介面以拟,用其内置之器,可成可持、可恃之试套,随代码之基而增。

此试之常法,施于诸般Go之用,自网务至CLI之用,建于Cobra者。与 Viper。测试命令行工具,需循此道而更重输入输出与标志解析之验。

始以简易之验,渐增覆盖之广,须知验之为道,实乃投资于代码之质与开发者之信。Go之社群重验,故持项目久远易,协众人之效亦佳。

之应用架构枢,,以寻关于Go项目结构、依赖注入、API设计及集成模式之相关指南。