go-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Testing
Go测试
The package provides support for automated testing of Go packages. Tests are run with and require no external libraries — the standard library covers unit tests, benchmarks, fuzz tests, and example functions.
testinggo testtestinggo testFile and Function Conventions
文件与函数约定
Test files must end in . They are excluded from normal builds but included by .
_test.gogo testTest functions must match the signature where does not start with a lowercase letter:
func TestXxx(*testing.T)Xxxgo
package mypackage
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
if got != 5 {
t.Errorf("Add(2, 3) = %d; want 5", got)
}
}测试文件必须以结尾。它们会被排除在常规构建之外,但会被包含。
_test.gogo test测试函数必须符合的签名,其中Xxx不能以小写字母开头:
func TestXxx(*testing.T)go
package mypackage
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
if got != 5 {
t.Errorf("Add(2, 3) = %d; want 5", got)
}
}White-box vs Black-box Tests
白盒测试 vs 黑盒测试
- Same package () — accesses unexported identifiers
package mypackage - suffix package (
_test) — tests only the exported API; this is "black-box" testingpackage mypackage_test
Both styles can coexist in the same directory.
- 同包()——可访问未导出标识符
package mypackage - 带后缀的包(
_test)——仅测试导出的API;这属于“黑盒测试”package mypackage_test
两种测试风格可以共存于同一目录中。
Reporting Failures
报告测试失败
| Method | Behaviour |
|---|---|
| Marks failed, continues execution |
| Marks failed, continues execution |
| Marks failed, stops test immediately |
| Marks failed, stops test immediately |
| Marks failed without logging, continues |
| Marks failed without logging, stops immediately |
Use / when checking multiple independent conditions so all failures are reported. Use / when a failure makes further checks meaningless (e.g., a nil pointer).
ErrorfErrorFatalfFatalPrefer over unless subsequent assertions depend on a prior one succeeding.
t.Errorft.Fatalf| 方法 | 行为 |
|---|---|
| 标记测试失败,继续执行 |
| 标记测试失败,继续执行 |
| 标记测试失败,立即停止测试 |
| 标记测试失败,立即停止测试 |
| 标记测试失败但不记录日志,继续执行 |
| 标记测试失败但不记录日志,立即停止 |
当检查多个独立条件时,使用/,这样所有失败都会被报告。当某个失败会导致后续检查失去意义时(例如空指针),使用/。
ErrorfErrorFatalfFatal除非后续断言依赖于前一个断言的成功,否则优先使用而非。
t.Errorft.FatalfTable-Driven Tests
表驱动测试
Table-driven tests are the idiomatic Go pattern for testing a function against many inputs:
go
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{name: "positive", a: 10, b: 2, want: 5},
{name: "negative divisor", a: 10, b: -2, want: -5},
{name: "divide by zero", a: 10, b: 0, wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := Divide(tc.a, tc.b)
if (err != nil) != tc.wantErr {
t.Fatalf("Divide(%v, %v) error = %v, wantErr %v", tc.a, tc.b, err, tc.wantErr)
}
if !tc.wantErr && got != tc.want {
t.Errorf("Divide(%v, %v) = %v; want %v", tc.a, tc.b, got, tc.want)
}
})
}
}Name each case descriptively. Use so each case appears as a named subtest in output and can be run individually.
t.Run表驱动测试是Go语言中针对多组输入测试函数的惯用模式:
go
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{name: "positive", a: 10, b: 2, want: 5},
{name: "negative divisor", a: 10, b: -2, want: -5},
{name: "divide by zero", a: 10, b: 0, wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := Divide(tc.a, tc.b)
if (err != nil) != tc.wantErr {
t.Fatalf("Divide(%v, %v) error = %v, wantErr %v", tc.a, tc.b, err, tc.wantErr)
}
if !tc.wantErr && got != tc.want {
t.Errorf("Divide(%v, %v) = %v; want %v", tc.a, tc.b, got, tc.want)
}
})
}
}为每个测试用例起一个有描述性的名称。使用让每个用例在输出中显示为命名子测试,并且可以单独运行。
t.RunSubtests
子测试
t.Run(name, func(t *testing.T))- Appear in output as
TestParent/SubName - Can be run individually:
go test -run TestParent/SubName - Share setup/teardown with the parent
- Can be run in parallel independently of other top-level tests
go
func TestAPI(t *testing.T) {
server := startTestServer(t) // shared setup
t.Run("GET /users", func(t *testing.T) {
// ...
})
t.Run("POST /users", func(t *testing.T) {
// ...
})
// server is cleaned up after all subtests finish
}t.Run(name, func(t *testing.T))- 在输出中显示为
TestParent/SubName - 可单独运行:
go test -run TestParent/SubName - 与父测试共享初始化/清理操作
- 可独立于其他顶级测试并行运行
go
func TestAPI(t *testing.T) {
server := startTestServer(t) // 共享初始化
t.Run("GET /users", func(t *testing.T) {
// ...
})
t.Run("POST /users", func(t *testing.T) {
// ...
})
// 所有子测试完成后,server会被清理
}Parallel Tests
并行测试
Call at the start of a test function to allow it to run concurrently with other parallel tests:
t.Parallel()go
func TestExpensive(t *testing.T) {
t.Parallel()
// ...
}For parallel subtests in a table-driven test, capture the loop variable:
go
for _, tc := range tests {
tc := tc // capture range variable (required before Go 1.22)
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// use tc safely
})
}From Go 1.22 onward, loop variable capture is automatic and the line is no longer needed.
tc := tc在测试函数开头调用,允许该测试与其他并行测试同时运行:
t.Parallel()go
func TestExpensive(t *testing.T) {
t.Parallel()
// ...
}对于表驱动测试中的并行子测试,需要捕获循环变量:
go
for _, tc := range tests {
tc := tc // 捕获循环变量(Go 1.22之前是必需的)
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// 安全使用tc
})
}从Go 1.22开始,循环变量会自动被捕获,不再需要这一行。
tc := tcCleanup
清理操作
t.Cleanup(f func())go
func TestWithDB(t *testing.T) {
db := openTestDB(t)
t.Cleanup(func() { db.Close() })
// test body — db.Close is called automatically when test ends
}Prefer over inside test helpers because it runs after all subtests complete, not just when the helper function returns.
t.Cleanupdefert.Cleanup(f func())go
func TestWithDB(t *testing.T) {
db := openTestDB(t)
t.Cleanup(func() { db.Close() })
// 测试主体——测试结束时会自动调用db.Close()
}在测试辅助函数中,优先使用而非,因为它会在所有子测试完成后运行,而不是在辅助函数返回时运行。
t.CleanupdeferTest Helpers
测试辅助函数
Mark a function as a test helper with so error output points to the call site, not inside the helper:
t.Helper()go
func assertEqualInts(t *testing.T, got, want int) {
t.Helper() // makes error line point to the caller
if got != want {
t.Errorf("got %d; want %d", got, want)
}
}Always call as the first statement in helper functions.
t.Helper()使用将函数标记为测试辅助函数,这样错误输出会指向调用该辅助函数的位置,而非辅助函数内部:
t.Helper()go
func assertEqualInts(t *testing.T, got, want int) {
t.Helper() // 让错误行指向调用者
if got != want {
t.Errorf("got %d; want %d", got, want)
}
}在辅助函数中,应始终将作为第一条语句调用。
t.Helper()Temporary Directories
临时目录
t.TempDir()go
func TestWriteFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "output.txt")
// write to path — dir is cleaned up automatically
}t.TempDir()go
func TestWriteFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "output.txt")
// 向path写入内容——测试结束时dir会被自动清理
}Environment Variables
环境变量
t.Setenv(key, value)go
func TestWithEnv(t *testing.T) {
t.Setenv("MY_CONFIG", "test-value")
// original value restored after test
}t.Setenv(key, value)go
func TestWithEnv(t *testing.T) {
t.Setenv("MY_CONFIG", "test-value")
// 测试结束后会恢复原始值
}Skipping Tests
跳过测试
Skip a test conditionally using , , or :
t.Skipt.Skipft.SkipNowgo
func TestIntegration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode")
}
// ...
}
func TestRequiresDocker(t *testing.T) {
if os.Getenv("DOCKER_HOST") == "" {
t.Skip("DOCKER_HOST not set")
}
// ...
}使用、或可以有条件地跳过测试:
t.Skipt.Skipft.SkipNowgo
func TestIntegration(t *testing.T) {
if testing.Short() {
t.Skip("在短模式下跳过集成测试")
}
// ...
}
func TestRequiresDocker(t *testing.T) {
if os.Getenv("DOCKER_HOST") == "" {
t.Skip("未设置DOCKER_HOST")
}
// ...
}Example Functions
示例函数
Example functions serve as documentation and are verified by :
go testgo
func ExampleAdd() {
fmt.Println(Add(1, 2))
// Output: 3
}The comment is compared against stdout. Examples without an output comment are compiled but not executed. Use when output order is non-deterministic.
// Output:// Unordered output:Naming conventions:
go
func Example() { ... } // package example
func ExampleAdd() { ... } // function Add
func ExampleCalc() { ... } // type Calc
func ExampleCalc_Add() { ... } // method Calc.Add
func ExampleAdd_second() { ... } // second example for Add (suffix starts lowercase)示例函数可作为文档使用,并且会被验证:
go testgo
func ExampleAdd() {
fmt.Println(Add(1, 2))
// Output: 3
}// Output:// Unordered output:命名约定:
go
func Example() { ... } // 包级示例
func ExampleAdd() { ... } // 针对Add函数的示例
func ExampleCalc() { ... } // 针对Calc类型的示例
func ExampleCalc_Add() { ... } // 针对Calc.Add方法的示例
func ExampleAdd_second() { ... } // Add函数的第二个示例(后缀以小写字母开头)TestMain
TestMain
TestMain_test.gogo
func TestMain(m *testing.M) {
// setup
code := m.Run()
// teardown
os.Exit(code)
}Use for package-level resources (database connections, server processes). It is not necessary for per-test resources — use instead.
TestMaint.CleanupTestMain_test.gogo
func TestMain(m *testing.M) {
// 初始化操作
code := m.Run()
// 清理操作
os.Exit(code)
}TestMainTestMaint.CleanupRunning Tests
运行测试
bash
undefinedbash
undefinedRun all tests in the current module
运行当前模块中的所有测试
go test ./...
go test ./...
Run with verbose output
运行测试并显示详细输出
go test -v ./...
go test -v ./...
Run tests matching a pattern (regexp)
运行匹配指定模式的测试(正则表达式)
go test -run TestAdd ./...
go test -run TestAdd ./...
Run a specific subtest
运行特定的子测试
go test -run TestDivide/divide_by_zero ./...
go test -run TestDivide/divide_by_zero ./...
Run with race detector
启用竞争检测器运行测试
go test -race ./...
go test -race ./...
Run with coverage
运行测试并统计覆盖率
go test -cover ./...
go test -cover ./...
Generate HTML coverage report
生成HTML格式的覆盖率报告
go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out
go test -coverprofile=coverage.out ./... && go tool cover -html=coverage.out
Skip slow tests
跳过慢测试
go test -short ./...
go test -short ./...
Set test timeout
设置测试超时时间
go test -timeout 30s ./...
go test -timeout 30s ./...
Run benchmarks
运行基准测试
go test -bench=. ./...
go test -bench=. ./...
Run benchmarks with memory allocation stats
运行基准测试并显示内存分配统计
go test -bench=. -benchmem ./...
undefinedgo test -bench=. -benchmem ./...
undefinedQuick Reference: testing.T
Methods
testing.T快速参考:testing.T
方法
testing.T| Method | Purpose |
|---|---|
| Create named subtest |
| Mark test as parallel |
| Mark as helper function |
| Register teardown function |
| Create auto-cleaned temp directory |
| Set env var, auto-restored after test |
| Change working dir, auto-restored |
| Context canceled before cleanup runs |
| Log (shown on failure or with |
| Log formatted |
| Fail + log, continue |
| Fail + log formatted, continue |
| Fail + log, stop |
| Fail + log formatted, stop |
| Skip + log, stop |
| Skip + log formatted, stop |
| Return full test name |
| Reports whether test has failed |
| Returns test deadline from |
| 方法 | 用途 |
|---|---|
| 创建命名子测试 |
| 将测试标记为可并行运行 |
| 将函数标记为辅助函数 |
| 注册清理函数 |
| 创建自动清理的临时目录 |
| 设置环境变量,测试结束后自动恢复 |
| 切换工作目录,测试结束后自动恢复 |
| 在清理操作运行前取消的上下文 |
| 记录日志(测试失败或使用 |
| 记录格式化日志 |
| 标记测试失败并记录日志,继续执行 |
| 标记测试失败并记录格式化日志,继续执行 |
| 标记测试失败并记录日志,停止执行 |
| 标记测试失败并记录格式化日志,停止执行 |
| 跳过测试并记录日志,停止执行 |
| 跳过测试并记录格式化日志,停止执行 |
| 返回完整的测试名称 |
| 报告测试是否已失败 |
| 返回通过 |
Additional Resources
额外资源
For benchmarks, fuzz testing, and advanced patterns:
- —
references/benchmarks-and-fuzzing.mdAPI,testing.Bstyle, parallel benchmarks,b.Loop()fuzz tests,testing.Fpatterns, andTestMainAllocsPerRun
关于基准测试、模糊测试和高级模式的内容,请参考:
- ——
references/benchmarks-and-fuzzing.mdAPI、testing.B风格、并行基准测试、b.Loop()模糊测试、testing.F模式和TestMainAllocsPerRun