Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

testing 包详解

概述

testing 包提供了对 Go 包自动化测试的支持。它旨在与 go test 命令一起使用,该命令自动执行任何形式为 func TestXxx(*testing.T) 的函数。

核心功能

  • 单元测试(Test 函数)
  • 基准测试(Benchmark 函数)
  • 模糊测试(Fuzz 函数,Go 1.18+)
  • 示例测试(Example 函数)
  • 子测试和子基准测试
  • 测试覆盖率
  • 并行测试
  • 测试日志和报告

重要说明

  • Go 版本:所有 Go 版本都支持
  • 测试函数命名:必须以 TestBenchmarkFuzzExample 开头
  • 文件命名:测试文件必须以 _test.go 结尾
  • ⚠️ 跳过机制:使用 -short 标志可跳过耗时测试

包导入

import "testing"

常量

测试类型常量

// 测试函数命名规则
func TestXxx(t *testing.T)        // 单元测试(Xxx 首字母大写)
func BenchmarkXxx(b *testing.B)   // 基准测试
func FuzzXxx(f *testing.F)        // 模糊测试(Go 1.18+)
func ExampleXxx()                 // 示例测试

接口

TB

type TB interface {
    // 常用方法
    Cleanup(f func())
    Error(args ...any)
    Errorf(format string, args ...any)
    Fail()
    FailNow()
    Failed() bool
    Fatal(args ...any)
    Fatalf(format string, args ...any)
    Helper()
    Log(args ...any)
    Logf(format string, args ...any)
    Name() string
    Skip(args ...any)
    SkipNow()
    Skipf(format string, args ...any)
    Skipped() bool
    TempDir() string
    
    // Go 1.17+
    Setenv(key, value string)
    Chdir(dir string)
    
    // Go 1.18+
    Context() context.Context
    ArtifactDir() string
    Attr(key, value string)
}

功能: T、B 和 F 共用的接口。

类型详解(按 A-Z 分类)

B

type B struct {
    // 包含过滤或未导出的字段
}

功能: 传递给 Benchmark 函数的类型,用于管理基准测试计时和控制迭代次数。

特点

  • 基准测试在 Benchmark 函数返回或调用 FailNow、Fatal、Fatalf、SkipNow、Skip、Skipf 时结束
  • 这些方法必须从运行 Benchmark 函数的 goroutine 调用
  • 报告方法(Log、Error 等)可以从多个 goroutine 同时调用

方法

  • ArtifactDir() string - 获取输出目录(Go 1.24+)
  • Attr(key, value string) - 发射测试属性(Go 1.24+)
  • Chdir(dir string) - 改变工作目录
  • Cleanup(f func()) - 注册清理函数
  • Context() context.Context - 获取 context(Go 1.18+)
  • Elapsed() time.Duration - 获取经过时间(Go 1.24+)
  • Error(args ...any) - 记录错误
  • Errorf(format string, args ...any) - 记录格式化错误
  • Fail() - 标记失败
  • FailNow() - 标记失败并停止
  • Failed() bool - 检查是否失败
  • Fatal(args ...any) - 记录致命错误并停止
  • Fatalf(format string, args ...any) - 记录格式化致命错误
  • Helper() - 标记为辅助函数
  • Log(args ...any) - 记录日志
  • Logf(format string, args ...any) - 记录格式化日志
  • Loop() bool - 基准循环(Go 1.24+)
  • Name() string - 获取名称
  • Output() io.Writer - 获取输出写入器
  • ReportAllocs() - 报告内存分配
  • ReportMetric(n float64, unit string) - 报告指标(Go 1.18+)
  • ResetTimer() - 重置计时器
  • Run(name string, f func(b *B)) bool - 运行子基准
  • RunParallel(body func(*PB)) - 并行运行
  • SetBytes(n int64) - 设置字节数
  • SetParallelism(p int) - 设置并行度
  • Setenv(key, value string) - 设置环境变量
  • Skip(args ...any) - 跳过
  • SkipNow() - 立即跳过
  • Skipf(format string, args ...any) - 格式化跳过
  • Skipped() bool - 检查是否跳过
  • StartTimer() - 开始计时
  • StopTimer() - 停止计时
  • TempDir() string - 获取临时目录

示例

package main

import (
    "testing"
)

func BenchmarkExample(b *testing.B) {
    // 设置代码(不计入测量)
    data := make([]int, 1000)
    
    // 基准循环(Go 1.24+ 推荐)
    for b.Loop() {
        // 要测量的代码
        sum := 0
        for _, v := range data {
            sum += v
        }
    }
    
    // 清理代码(不计入测量)
}

B.Loop

func (b *B) Loop() bool

功能: 只要基准测试应该继续运行就返回 true。

注意

  • Go 1.24+ 推荐使用
  • 首次调用时自动重置计时器
  • 返回 false 时停止计时器
  • 循环内变量会被 KeepAlive 防止优化

示例

func BenchmarkLoop(b *testing.B) {
    for b.Loop() {
        // 要测量的代码
    }
}

B.ReportAllocs

func (b *B) ReportAllocs()

功能: 启用内存分配统计。

等价于: 设置 -test.benchmem 标志,但只影响调用 ReportAllocs 的基准函数。

示例

func BenchmarkAllocs(b *testing.B) {
    b.ReportAllocs()
    for b.Loop() {
        _ = make([]int, 100)
    }
}

B.ReportMetric

func (b *B) ReportMetric(n float64, unit string)

功能: 添加自定义指标到基准测试结果。

参数

  • n float64 - 指标值
  • unit string - 单位(如 “allocs/op”、“ns/op”)

注意

  • 如果单位是每迭代,应该除以 b.N
  • 按惯例单位应以 “/op” 结尾
  • 会覆盖同名的之前报告的值

示例

func BenchmarkCustomMetric(b *testing.B) {
    customCount := 0
    for b.Loop() {
        customCount++
    }
    b.ReportMetric(float64(customCount)/b.N, "custom/op")
}

B.ResetTimer

func (b *B) ResetTimer()

功能: 清零基准测试的经过时间和内存分配计数器。

注意

  • 不影响计时器是否运行
  • 删除用户报告的指标

示例

func BenchmarkReset(b *testing.B) {
    // 昂贵设置
    big := NewBig()
    
    b.ResetTimer()
    
    for i := 0; i < b.N; i++ {
        big.Len()
    }
}

B.Run

func (b *B) Run(name string, f func(b *B)) bool

功能: 运行子基准测试。

参数

  • name string - 子基准名称
  • f func(b *B) - 子基准函数

返回值

  • bool - 是否成功

注意

  • 调用 Run 的基准不会被测量
  • 会被调用一次且 N=1

示例

func BenchmarkTable(b *testing.B) {
    tests := []struct {
        name string
        size int
    }{
        {"small", 100},
        {"large", 10000},
    }
    
    for _, tt := range tests {
        b.Run(tt.name, func(b *testing.B) {
            data := make([]int, tt.size)
            for b.Loop() {
                _ = data
            }
        })
    }
}

B.RunParallel

func (b *B) RunParallel(body func(*PB))

功能: 并行运行基准测试。

参数

  • body func(*PB) - 每个 goroutine 的执行体

注意

  • 创建多个 goroutine 并分配 b.N 次迭代
  • goroutine 数量默认为 GOMAXPROCS
  • 通常与 go test -cpu 一起使用

示例

func BenchmarkParallel(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        var buf bytes.Buffer
        for pb.Next() {
            buf.Reset()
            // 处理
        }
    })
}

B.SetBytes

func (b *B) SetBytes(n int64)

功能: 记录单次操作处理的字节数。

参数

  • n int64 - 字节数

注意

  • 如果调用,基准将报告 ns/op 和 MB/s

示例

func BenchmarkRead(b *testing.B) {
    data := make([]byte, 1024)
    b.SetBytes(int64(len(data)))
    
    for b.Loop() {
        _ = data
    }
}

B.StartTimer / B.StopTimer

func (b *B) StartTimer()
func (b *B) StopTimer()

功能: 开始/停止基准测试计时。

注意

  • 基准测试开始前自动调用 StartTimer
  • 可用于暂停测量不相关的代码

示例

func BenchmarkPause(b *testing.B) {
    for i := 0; i < b.N; i++ {
        b.StopTimer()
        // 准备数据(不测量)
        b.StartTimer()
        // 处理(测量)
    }
}

BenchmarkResult

type BenchmarkResult struct {
    N         int           // 迭代次数
    T         time.Duration // 总时间
    Bytes     int64         // 字节数
    MemAllocs uint64        // 分配次数
    MemBytes  uint64        // 分配字节数
    // ... 更多字段
}

功能: 包含基准测试运行结果。

方法

  • AllocedBytesPerOp() int64 - 返回 “B/op”
  • AllocsPerOp() int64 - 返回 “allocs/op”
  • MemString() string - 返回内存分配字符串
  • NsPerOp() int64 - 返回 “ns/op”
  • String() string - 返回结果摘要

Benchmark

func Benchmark(f func(b *B)) BenchmarkResult

功能: 基准测试单个函数。

用途: 创建不使用 go test 命令的自定义基准测试。

示例

result := testing.Benchmark(func(b *testing.B) {
    for b.Loop() {
        // 基准代码
    }
})
fmt.Println(result)

F

type F struct {
    // 包含过滤或未导出的字段
}

功能: 传递给模糊测试函数的类型。

特点

  • Go 1.18+
  • 模糊测试运行随机生成的输入以查找 bug
  • 维护种子语料库(seed corpus)

方法

  • Add(args ...any) - 添加种子输入
  • ArtifactDir() string - 获取输出目录
  • Attr(key, value string) - 发射测试属性
  • Cleanup(f func()) - 注册清理函数
  • Context() context.Context - 获取 context
  • Error(args ...any) - 记录错误
  • Errorf(format string, args ...any) - 记录格式化错误
  • Fail() - 标记失败
  • FailNow() - 标记失败并停止
  • Failed() bool - 检查是否失败
  • Fatal(args ...any) - 记录致命错误
  • Fatalf(format string, args ...any) - 记录格式化致命错误
  • Fuzz(ff any) - 运行模糊函数
  • Helper() - 标记为辅助函数
  • Log(args ...any) - 记录日志
  • Logf(format string, args ...any) - 记录格式化日志
  • Name() string - 获取名称
  • Output() io.Writer - 获取输出写入器
  • Setenv(key, value string) - 设置环境变量
  • Skip(args ...any) - 跳过
  • SkipNow() - 立即跳过
  • Skipf(format string, args ...any) - 格式化跳过
  • Skipped() bool - 检查是否跳过
  • TempDir() string - 获取临时目录

示例

func FuzzHex(f *testing.F) {
    // 添加种子输入
    for _, seed := range [][]byte{{}, {0}, {1, 2, 3}} {
        f.Add(seed)
    }
    
    // 定义模糊目标
    f.Fuzz(func(t *testing.T, in []byte) {
        enc := hex.EncodeToString(in)
        out, err := hex.DecodeString(enc)
        if err != nil {
            t.Fatalf("%v: decode: %v", in, err)
        }
        if !bytes.Equal(in, out) {
            t.Fatalf("%v: not equal", in)
        }
    })
}

F.Add

func (f *F) Add(args ...any)

功能: 将参数添加到模糊测试的种子语料库。

参数

  • args ...any - 种子输入

注意

  • 必须在 F.Fuzz 之前调用
  • 类型必须与模糊目标参数匹配

F.Fuzz

func (f *F) Fuzz(ff any)

功能: 运行模糊函数 ff。

参数

  • ff any - 模糊目标函数

要求

  • 第一个参数必须是 *T
  • 剩余参数是模糊类型([]byte、string、bool、数字等)
  • 无返回值

注意

  • 模糊模式下,发现问题、超时或被中断前不返回
  • 非模糊模式下,只运行种子输入

M

type M struct {
    // 包含过滤或未导出的字段
}

功能: 传递给 TestMain 函数的类型,用于运行实际测试。

方法

  • Run() (code int) - 运行测试并返回退出码

示例

func TestMain(m *testing.M) {
    // 设置代码
    flag.Parse()
    
    // 运行测试
    code := m.Run()
    
    // 清理代码
    os.Exit(code)
}

MainStart

func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M

功能: 供 go test 生成的测试使用。

注意

  • Go 1.4+
  • 不供直接调用
  • 不受 Go 1 兼容性保证

PB

type PB struct {
    // 包含过滤或未导出的字段
}

功能: 由 RunParallel 用于运行并行基准测试。

方法

  • Next() bool - 报告是否有更多迭代要执行

示例

b.RunParallel(func(pb *testing.PB) {
    for pb.Next() {
        // 处理
    }
})

T

type T struct {
    // 包含过滤或未导出的字段
}

功能: 传递给 Test 函数的类型,用于管理测试状态和支持格式化的测试日志。

特点

  • 测试在 Test 函数返回或调用 FailNow、Fatal、Fatalf、SkipNow、Skip、Skipf 时结束
  • 这些方法以及 Parallel 必须从运行 Test 函数的 goroutine 调用
  • 报告方法(Log、Error 等)可以从多个 goroutine 同时调用

方法

  • ArtifactDir() string - 获取输出目录(Go 1.24+)
  • Attr(key, value string) - 发射测试属性(Go 1.24+)
  • Chdir(dir string) - 改变工作目录
  • Cleanup(f func()) - 注册清理函数
  • Context() context.Context - 获取 context(Go 1.18+)
  • Deadline() (deadline time.Time, ok bool) - 获取截止时间(Go 1.15+)
  • Error(args ...any) - 记录错误
  • Errorf(format string, args ...any) - 记录格式化错误
  • Fail() - 标记失败
  • FailNow() - 标记失败并停止
  • Failed() bool - 检查是否失败
  • Fatal(args ...any) - 记录致命错误并停止
  • Fatalf(format string, args ...any) - 记录格式化致命错误
  • Helper() - 标记为辅助函数
  • Log(args ...any) - 记录日志
  • Logf(format string, args ...any) - 记录格式化日志
  • Name() string - 获取名称
  • Output() io.Writer - 获取输出写入器
  • Parallel() - 标记为并行测试
  • Run(name string, f func(t *T)) bool - 运行子测试
  • Setenv(key, value string) - 设置环境变量
  • Skip(args ...any) - 跳过
  • SkipNow() - 立即跳过
  • Skipf(format string, args ...any) - 格式化跳过
  • Skipped() bool - 检查是否跳过
  • TempDir() string - 获取临时目录

示例

func TestExample(t *testing.T) {
    got := MyFunction()
    want := 42
    
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }
}

T.Cleanup

func (c *T) Cleanup(f func())

功能: 注册一个函数,在测试(或子测试)及其所有子测试完成时调用。

注意

  • 清理函数按后进先出顺序调用
  • 可用于资源清理

示例

func TestCleanup(t *testing.T) {
    // 设置
    file, _ := os.Create("/tmp/test")
    
    t.Cleanup(func() {
        file.Close()
        os.Remove("/tmp/test")
    })
    
    // 测试代码
}

T.Context

func (c *T) Context() context.Context

功能: 返回一个 context,在 Cleanup 注册的函数被调用前取消。

注意

  • Go 1.18+
  • Cleanup 函数可以等待在 context.Context.Done 上关闭的资源

示例

func TestContext(t *testing.T) {
    ctx := t.Context()
    
    go func() {
        <-ctx.Done()
        t.Log("Context canceled")
    }()
    
    // 测试代码
}

T.Parallel

func (t *T) Parallel()

功能: 标记此测试为并行运行。

注意

  • 只与其他并行测试并行运行
  • 必须从测试 goroutine 调用

示例

func TestParallel1(t *testing.T) {
    t.Parallel()
    // 并行测试代码
}

func TestParallel2(t *testing.T) {
    t.Parallel()
    // 并行测试代码
}

T.Run

func (t *T) Run(name string, f func(t *T)) bool

功能: 运行子测试。

参数

  • name string - 子测试名称
  • f func(t *T) - 子测试函数

返回值

  • bool - 是否成功

注意

  • 在单独的 goroutine 中运行
  • 支持表驱动测试
  • 可以控制并行性

示例

func TestTable(t *testing.T) {
    tests := []struct {
        name string
        input int
        want int
    }{
        {"positive", 5, 5},
        {"negative", -5, 5},
        {"zero", 0, 0},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := abs(tt.input)
            if got != tt.want {
                t.Errorf("abs(%d) = %d, want %d", tt.input, got, tt.want)
            }
        })
    }
}

T.TempDir

func (c *T) TempDir() string

功能: 返回一个临时目录供测试使用。

返回值

  • string - 临时目录路径

注意

  • 测试完成后自动删除
  • 每次调用返回唯一目录
  • 如果创建失败会调用 Fatal

示例

func TestTempDir(t *testing.T) {
    dir := t.TempDir()
    
    // 在 dir 中创建文件
    file := filepath.Join(dir, "test.txt")
    os.WriteFile(file, []byte("test"), 0644)
    
    // 测试完成后 dir 自动删除
}

ValueError

type ValueError struct {
    Method string
    Type   Type
}

功能: 当在 Value 上调用不支持的方法时发生。

方法

  • Error() string - 实现 error 接口

函数详解(按 A-Z 分类)

A

AllocsPerRun

func AllocsPerRun(runs int, f func()) (avg float64)

功能: 返回调用 f 期间的平均分配次数。

参数

  • runs int - 运行次数
  • f func() - 要测量的函数

返回值

  • avg float64 - 平均分配次数

注意

  • 返回值虽然是 float64,但总是整数值
  • 先运行一次作为预热
  • 运行期间设置 GOMAXPROCS 为 1

示例

avg := testing.AllocsPerRun(100, func() {
    _ = make([]int, 100)
})
fmt.Printf("平均分配:%.0f 次\n", avg)

C

CoverMode

func CoverMode() string

功能: 报告测试覆盖率模式设置为什么。

返回值

  • string - 模式(“set”、“count”、“atomic”)

注意

  • Go 1.8+
  • 如果未启用覆盖率返回空字符串

Coverage

func Coverage() float64

功能: 报告当前代码覆盖率(0-1 之间的分数)。

返回值

  • float64 - 覆盖率

注意

  • Go 1.4+
  • 如果未启用覆盖率返回 0
  • 不替代 go test -cover 生成的报告

I

Init

func Init()

功能: 注册测试标志。

注意

  • go test 命令在运行测试函数前自动注册
  • 只有在不使用 go test 时调用 Benchmark 等函数才需要

R

RegisterCover

func RegisterCover(c Cover)

功能: 记录测试的覆盖率累加器。

注意

  • 内部函数
  • 可能变化

RunBenchmarks

func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)

功能: 运行基准测试。

注意

  • 内部函数

RunExamples

func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)

功能: 运行示例测试。

注意

  • 内部函数

RunTests

func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)

功能: 运行测试。

注意

  • 内部函数

S

Short

func Short() bool

功能: 报告是否设置了 -test.short 标志。

返回值

  • bool - 是否为短模式

示例

func TestLongRunning(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping in short mode")
    }
    // 耗时测试
}

T

Testing

func Testing() bool

功能: 报告当前代码是否在测试中运行。

返回值

  • bool - 是否在测试中

示例

func init() {
    if testing.Testing() {
        // 测试环境特殊初始化
    }
}

V

Verbose

func Verbose() bool

功能: 报告是否设置了 -test.v 标志。

返回值

  • bool - 是否为详细模式

典型示例

示例 1:基本单元测试

package main

import (
    "testing"
)

func Add(a, b int) int {
    return a + b
}

func TestAdd(t *testing.T) {
    tests := []struct {
        name string
        a    int
        b    int
        want int
    }{
        {"positive", 1, 2, 3},
        {"negative", -1, -2, -3},
        {"zero", 0, 0, 0},
        {"mixed", -1, 1, 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)
            }
        })
    }
}

示例 2:表驱动测试

func TestAbs(t *testing.T) {
    tests := []struct {
        name  string
        input int
        want  int
    }{
        {"positive", 5, 5},
        {"negative", -5, 5},
        {"zero", 0, 0},
        {"max int", int(^uint(0)>>1), int(^uint(0)>>1)},
        {"min int", -int(^uint(0)>>1) - 1, int(^uint(0)>>1) + 1},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := abs(tt.input)
            if got != tt.want {
                t.Errorf("abs(%d) = %d, want %d", tt.input, got, tt.want)
            }
        })
    }
}

示例 3:并行测试

func TestParallel(t *testing.T) {
    tests := []struct {
        name  string
        input int
    }{
        {"test1", 1},
        {"test2", 2},
        {"test3", 3},
        {"test4", 4},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()
            
            // 并行测试代码
            result := expensiveOperation(tt.input)
            if result != expected {
                t.Fail()
            }
        })
    }
}

示例 4:基准测试

func BenchmarkAdd(b *testing.B) {
    for b.Loop() {
        Add(1, 2)
    }
}

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

func BenchmarkAddParallel(b *testing.B) {
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            Add(1, 2)
        }
    })
}

func BenchmarkAddWithSetup(b *testing.B) {
    // 设置(不测量)
    a, b := 1, 2
    
    b.ResetTimer()
    
    for i := 0; i < b.N; i++ {
        Add(a, b)
    }
}

示例 5:模糊测试

func FuzzAdd(f *testing.F) {
    // 添加种子
    f.Add(int32(1), int32(2))
    f.Add(int32(-1), int32(-2))
    f.Add(int32(0), int32(0))
    
    // 模糊目标
    f.Fuzz(func(t *testing.T, a, b int32) {
        result := Add(int(a), int(b))
        
        // 验证
        expected := int(a) + int(b)
        if result != expected {
            t.Fatalf("Add(%d, %d) = %d, want %d", a, b, result, expected)
        }
    })
}

示例 6:示例测试

func ExampleAdd() {
    result := Add(2, 3)
    fmt.Println(result)
    // Output: 5
}

func ExampleAdd_negative() {
    result := Add(-1, -2)
    fmt.Println(result)
    // Output: -3
}

func ExampleAdd_multiple() {
    fmt.Println(Add(1, 2))
    fmt.Println(Add(3, 4))
    // Output:
    // 3
    // 7
}

示例 7:TestMain

var db *sql.DB

func TestMain(m *testing.M) {
    // 设置
    var err error
    db, err = sql.Open("postgres", "test-db")
    if err != nil {
        log.Fatal(err)
    }
    
    // 运行测试
    code := m.Run()
    
    // 清理
    db.Close()
    os.Exit(code)
}

示例 8:清理和临时资源

func TestWithCleanup(t *testing.T) {
    // 临时目录
    dir := t.TempDir()
    
    // 临时文件
    file := filepath.Join(dir, "test.txt")
    os.WriteFile(file, []byte("test"), 0644)
    
    // 清理函数
    t.Cleanup(func() {
        t.Log("Cleanup called")
    })
    
    // 测试代码
    data, _ := os.ReadFile(file)
    if string(data) != "test" {
        t.Fail()
    }
}

最佳实践

1. 使用表驱动测试

// ✅ 推荐
func TestFunction(t *testing.T) {
    tests := []struct {
        name   string
        input  int
        want   int
    }{
        {"case1", 1, 2},
        {"case2", 2, 3},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // 测试
        })
    }
}

// ❌ 不推荐:重复代码
func TestFunction(t *testing.T) {
    // 测试 case1
    // 测试 case2
    // ...
}

2. 使用子测试组织

// ✅ 推荐
func TestGroup(t *testing.T) {
    t.Run("SubTest1", func(t *testing.T) {
        // ...
    })
    t.Run("SubTest2", func(t *testing.T) {
        // ...
    })
}

// ❌ 不推荐:扁平结构
func TestGroupSubTest1(t *testing.T) { }
func TestGroupSubTest2(t *testing.T) { }

3. 使用 Cleanup 清理资源

// ✅ 推荐
func TestResource(t *testing.T) {
    file, _ := os.Create("/tmp/test")
    t.Cleanup(func() {
        file.Close()
        os.Remove("/tmp/test")
    })
}

// ❌ 不推荐:忘记清理
func TestResource(t *testing.T) {
    file, _ := os.Create("/tmp/test")
    // 忘记关闭
}

4. 使用 Helper 标记辅助函数

// ✅ 推荐
func assertEqual(t *testing.T, got, want int) {
    t.Helper()
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }
}

// ❌ 不推荐:错误位置指向辅助函数
func assertEqual(t *testing.T, got, want int) {
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }
}

5. 使用 TempDir 管理临时文件

// ✅ 推荐
func TestTemp(t *testing.T) {
    dir := t.TempDir()
    // 使用 dir,自动清理
}

// ❌ 不推荐:手动管理
func TestTemp(t *testing.T) {
    dir, _ := os.MkdirTemp("", "test")
    defer os.RemoveAll(dir) // 可能忘记
}

与其他包配合

与 os 包配合

func TestFile(t *testing.T) {
    dir := t.TempDir()
    file := filepath.Join(dir, "test.txt")
    
    // 创建文件
    os.WriteFile(file, []byte("test"), 0644)
    
    // 读取验证
    data, err := os.ReadFile(file)
    if err != nil {
        t.Fatal(err)
    }
    
    if string(data) != "test" {
        t.Errorf("got %q, want %q", data, "test")
    }
}

与 context 包配合

func TestContext(t *testing.T) {
    ctx, cancel := context.WithTimeout(t.Context(), time.Second)
    defer cancel()
    
    // 使用 ctx
    select {
    case <-ctx.Done():
        t.Log("Context done")
    case <-time.After(500 * time.Millisecond):
        t.Log("Operation completed")
    }
}

注意事项

限制

  1. 测试命名

    • Test 函数:首字母必须大写
    • Benchmark 函数:首字母必须大写
    • Fuzz 函数:首字母必须大写
    • Example 函数:大小写敏感
  2. 并发限制

    • Parallel 测试只与其他 Parallel 测试并行
    • Chdir、Setenv 不能在并行测试中使用
  3. 资源管理

    • 临时目录自动清理
    • Cleanup 按 LIFO 顺序调用

使用建议

  1. 测试文件命名

    # 正确
    foo_test.go
    
    # 错误
    test_foo.go
    
  2. 运行测试

    # 运行所有测试
    go test
    
    # 运行特定测试
    go test -run TestName
    
    # 运行基准测试
    go test -bench=.
    
    # 并行运行
    go test -parallel 4
    
    # 覆盖率
    go test -cover
    
    # 详细输出
    go test -v
    

快速参考

测试函数类型

类型签名运行命令
Testfunc TestXxx(t *testing.T)go test
Benchmarkfunc BenchmarkXxx(b *testing.B)go test -bench=.
Fuzzfunc FuzzXxx(f *testing.F)go test -fuzz=FuzzXxx
Examplefunc ExampleXxx()go test

T 方法分类

类别方法
失败Error, Errorf, Fail, FailNow, Fatal, Fatalf
跳过Skip, SkipNow, Skipf, Skipped
日志Log, Logf
辅助Helper, Name, Context
清理Cleanup, TempDir, Setenv, Chdir
子测试Run, Parallel
状态Failed, Skipped, Deadline

基准测试方法

方法功能
Loop()基准循环(Go 1.24+)
ResetTimer()重置计时器
StartTimer()开始计时
StopTimer()停止计时
ReportAllocs()报告分配
ReportMetric()报告指标
SetBytes()设置字节数
RunParallel()并行运行

常用命令

# 运行测试
go test
go test ./...
go test -v

# 运行特定测试
go test -run TestName
go test -run "Test.*"

# 基准测试
go test -bench=.
go test -bench=BenchmarkName
go test -benchmem

# 覆盖率
go test -cover
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

# 并行
go test -parallel 4
go test -cpu 1,2,4

# 模糊测试
go test -fuzz=FuzzName
go test -fuzztime=10s

# 其他
go test -short
go test -timeout 30s
go test -race
go test -count=5

总结

testing 包是 Go 标准库中用于自动化测试的核心包。

核心优势

  • ✅ 内置支持,无需第三方库
  • ✅ 完整的测试类型(单元、基准、模糊、示例)
  • ✅ 子测试支持表驱动测试
  • ✅ 并行测试提高速度
  • ✅ 覆盖率统计
  • ✅ 丰富的断言和日志方法

重要限制

  • ⚠️ 测试函数命名有严格要求
  • ⚠️ 某些方法只能从测试 goroutine 调用
  • ⚠️ 并行测试有限制

主要用途

  • 单元测试(Test 函数)
  • 性能基准测试(Benchmark 函数)
  • 模糊测试查找边界 bug(Fuzz 函数)
  • 文档示例验证(Example 函数)

使用建议

  1. 使用表驱动测试组织用例
  2. 使用子测试共享设置代码
  3. 使用 Cleanup 管理资源
  4. 使用 Helper 提高可读性
  5. 使用 TempDir 管理临时文件
  6. 使用 Parallel 提高测试速度

测试命令

go test -v -cover -race -parallel 4