go-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Testing

Go测试

The
testing
package provides support for automated testing of Go packages. Tests are run with
go test
and require no external libraries — the standard library covers unit tests, benchmarks, fuzz tests, and example functions.
testing
包为Go包的自动化测试提供支持。测试通过
go test
命令运行,无需外部库——标准库已涵盖单元测试、基准测试、模糊测试和示例函数。

File and Function Conventions

文件与函数约定

Test files must end in
_test.go
. They are excluded from normal builds but included by
go test
.
Test functions must match the signature
func TestXxx(*testing.T)
where
Xxx
does not start with a lowercase letter:
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)
    }
}
测试文件必须以
_test.go
结尾。它们会被排除在常规构建之外,但会被
go test
包含。
测试函数必须符合
func TestXxx(*testing.T)
的签名,其中Xxx不能以小写字母开头:
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 (
    package mypackage
    ) — accesses unexported identifiers
  • _test
    suffix package
    (
    package mypackage_test
    ) — tests only the exported API; this is "black-box" testing
Both styles can coexist in the same directory.
  • 同包
    package mypackage
    )——可访问未导出标识符
  • _test
    后缀的包
    package mypackage_test
    )——仅测试导出的API;这属于“黑盒测试”
两种测试风格可以共存于同一目录中。

Reporting Failures

报告测试失败

MethodBehaviour
t.Errorf(format, args...)
Marks failed, continues execution
t.Error(args...)
Marks failed, continues execution
t.Fatalf(format, args...)
Marks failed, stops test immediately
t.Fatal(args...)
Marks failed, stops test immediately
t.Fail()
Marks failed without logging, continues
t.FailNow()
Marks failed without logging, stops immediately
Use
Errorf
/
Error
when checking multiple independent conditions so all failures are reported. Use
Fatalf
/
Fatal
when a failure makes further checks meaningless (e.g., a nil pointer).
Prefer
t.Errorf
over
t.Fatalf
unless subsequent assertions depend on a prior one succeeding.
方法行为
t.Errorf(format, args...)
标记测试失败,继续执行
t.Error(args...)
标记测试失败,继续执行
t.Fatalf(format, args...)
标记测试失败,立即停止测试
t.Fatal(args...)
标记测试失败,立即停止测试
t.Fail()
标记测试失败但不记录日志,继续执行
t.FailNow()
标记测试失败但不记录日志,立即停止
当检查多个独立条件时,使用
Errorf
/
Error
,这样所有失败都会被报告。当某个失败会导致后续检查失去意义时(例如空指针),使用
Fatalf
/
Fatal
除非后续断言依赖于前一个断言的成功,否则优先使用
t.Errorf
而非
t.Fatalf

Table-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
t.Run
so each case appears as a named subtest in output and can be run individually.
表驱动测试是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.Run
让每个用例在输出中显示为命名子测试,并且可以单独运行。

Subtests

子测试

t.Run(name, func(t *testing.T))
creates a named subtest. Subtests:
  • 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
t.Parallel()
at the start of a test function to allow it to run concurrently with other parallel tests:
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
tc := tc
line is no longer needed.
在测试函数开头调用
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 := tc
这一行。

Cleanup

清理操作

t.Cleanup(f func())
registers a function to run after the test (and all its subtests) complete. Cleanup functions run in LIFO order.
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
t.Cleanup
over
defer
inside test helpers because it runs after all subtests complete, not just when the helper function returns.
t.Cleanup(f func())
用于注册一个在测试(及其所有子测试)完成后运行的函数。清理函数按照后进先出(LIFO)的顺序运行。
go
func TestWithDB(t *testing.T) {
    db := openTestDB(t)
    t.Cleanup(func() { db.Close() })
    // 测试主体——测试结束时会自动调用db.Close()
}
在测试辅助函数中,优先使用
t.Cleanup
而非
defer
,因为它会在所有子测试完成后运行,而不是在辅助函数返回时运行。

Test Helpers

测试辅助函数

Mark a function as a test helper with
t.Helper()
so error output points to the call site, not inside the 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
t.Helper()
as the first statement in helper functions.
使用
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()
creates a temporary directory that is automatically removed when the test completes:
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)
sets an environment variable and restores the original value after the test. Cannot be used in parallel tests.
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
t.Skip
,
t.Skipf
, or
t.SkipNow
:
go
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.Skip
t.Skipf
t.SkipNow
可以有条件地跳过测试:
go
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 test
:
go
func ExampleAdd() {
    fmt.Println(Add(1, 2))
    // Output: 3
}
The
// Output:
comment is compared against stdout. Examples without an output comment are compiled but not executed. Use
// Unordered output:
when output order is non-deterministic.
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 test
验证:
go
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
controls global test setup and teardown. Define it in any
_test.go
file in the package:
go
func TestMain(m *testing.M) {
    // setup
    code := m.Run()
    // teardown
    os.Exit(code)
}
Use
TestMain
for package-level resources (database connections, server processes). It is not necessary for per-test resources — use
t.Cleanup
instead.
TestMain
用于控制全局测试的初始化和清理操作。可以在包内任意
_test.go
文件中定义:
go
func TestMain(m *testing.M) {
    // 初始化操作
    code := m.Run()
    // 清理操作
    os.Exit(code)
}
TestMain
适用于包级资源(数据库连接、服务器进程)。对于每个测试的资源,不需要使用
TestMain
——使用
t.Cleanup
即可。

Running Tests

运行测试

bash
undefined
bash
undefined

Run 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 ./...
undefined
go test -bench=. -benchmem ./...
undefined

Quick Reference:
testing.T
Methods

快速参考:
testing.T
方法

MethodPurpose
t.Run(name, f)
Create named subtest
t.Parallel()
Mark test as parallel
t.Helper()
Mark as helper function
t.Cleanup(f)
Register teardown function
t.TempDir()
Create auto-cleaned temp directory
t.Setenv(k, v)
Set env var, auto-restored after test
t.Chdir(dir)
Change working dir, auto-restored
t.Context()
Context canceled before cleanup runs
t.Log(args...)
Log (shown on failure or with
-v
)
t.Logf(format, args...)
Log formatted
t.Error(args...)
Fail + log, continue
t.Errorf(format, args...)
Fail + log formatted, continue
t.Fatal(args...)
Fail + log, stop
t.Fatalf(format, args...)
Fail + log formatted, stop
t.Skip(args...)
Skip + log, stop
t.Skipf(format, args...)
Skip + log formatted, stop
t.Name()
Return full test name
t.Failed()
Reports whether test has failed
t.Deadline()
Returns test deadline from
-timeout
flag
方法用途
t.Run(name, f)
创建命名子测试
t.Parallel()
将测试标记为可并行运行
t.Helper()
将函数标记为辅助函数
t.Cleanup(f)
注册清理函数
t.TempDir()
创建自动清理的临时目录
t.Setenv(k, v)
设置环境变量,测试结束后自动恢复
t.Chdir(dir)
切换工作目录,测试结束后自动恢复
t.Context()
在清理操作运行前取消的上下文
t.Log(args...)
记录日志(测试失败或使用
-v
参数时显示)
t.Logf(format, args...)
记录格式化日志
t.Error(args...)
标记测试失败并记录日志,继续执行
t.Errorf(format, args...)
标记测试失败并记录格式化日志,继续执行
t.Fatal(args...)
标记测试失败并记录日志,停止执行
t.Fatalf(format, args...)
标记测试失败并记录格式化日志,停止执行
t.Skip(args...)
跳过测试并记录日志,停止执行
t.Skipf(format, args...)
跳过测试并记录格式化日志,停止执行
t.Name()
返回完整的测试名称
t.Failed()
报告测试是否已失败
t.Deadline()
返回通过
-timeout
参数设置的测试截止时间

Additional Resources

额外资源

For benchmarks, fuzz testing, and advanced patterns:
  • references/benchmarks-and-fuzzing.md
    testing.B
    API,
    b.Loop()
    style, parallel benchmarks,
    testing.F
    fuzz tests,
    TestMain
    patterns, and
    AllocsPerRun
关于基准测试、模糊测试和高级模式的内容,请参考:
  • references/benchmarks-and-fuzzing.md
    ——
    testing.B
    API、
    b.Loop()
    风格、并行基准测试、
    testing.F
    模糊测试、
    TestMain
    模式和
    AllocsPerRun