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/internal 包详解

概述

testing/internal 包目录包含 testing 包的内部实现支持。这些包主要用于测试执行的基础设施,通常由 go test 命令生成的代码自动使用,而不是由用户直接调用。

主要子包

  • testing/internal/testdeps:提供测试执行所需的依赖访问

重要说明

  • 这是内部包,不推荐直接使用
  • API 可能会在不通知的情况下更改
  • 主要由 go test 命令自动生成的代码使用

testing/internal/testdeps 包

概述

testdeps 包提供对测试执行所需依赖的访问。该包由生成的 main 包导入,它将 TestDeps 传递给 testing.Main。这样设计允许测试在运行时使用包,而无需将这些包作为 testing 包的直接依赖。

设计目的

  • 避免 testing 包直接依赖其他包
  • 使测试更容易编写
  • 支持覆盖率、模糊测试等高级功能

包导入

import "testing/internal/testdeps"

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

T

TestDeps

type TestDeps struct{}

作用:实现 testing.testDeps 接口,适合传递给 testing.MainStart

特点

  • 空结构体,所有方法都是值方法
  • 提供测试执行所需的各种功能
  • go test 生成的代码自动创建和使用

示例

// 这是 go test 生成的代码示例
var tests = []testing.InternalTest{
    {"TestExample", TestExample},
}

var deps testdeps.TestDeps
testing.Main(deps.MatchString, tests, nil, nil)

方法详解(按 A-Z 分层归类)

C

CheckCorpus

func (TestDeps) CheckCorpus(vals []any, types []reflect.Type) error

作用:验证语料库数据是否符合指定的类型

参数说明

  • vals:要验证的值切片
  • types:期望的类型切片

返回值

  • 如果验证失败,返回错误
  • 如果验证成功,返回 nil

Go 版本:Go 1.18+

示例

deps := testdeps.TestDeps{}

vals := []any{42, "hello", 3.14}
types := []reflect.Type{
    reflect.TypeOf(int(0)),
    reflect.TypeOf(""),
    reflect.TypeOf(float64(0)),
}

err := deps.CheckCorpus(vals, types)
if err != nil {
    // 处理错误
}

CoordinateFuzzing

func (TestDeps) CoordinateFuzzing(
    timeout time.Duration,
    limit int64,
    minimizeTimeout time.Duration,
    minimizeLimit int64,
    parallel int,
    seed []fuzz.CorpusEntry,
    types []reflect.Type,
    corpusDir, cacheDir string,
) (err error)

作用:协调模糊测试的执行

参数说明

  • timeout:模糊测试的超时时间
  • limit:测试次数限制
  • minimizeTimeout:最小化超时的时间
  • minimizeLimit:最小化次数限制
  • parallel:并行工作协程数
  • seed:种子语料库
  • types:模糊测试的类型
  • corpusDir:语料库目录
  • cacheDir:缓存目录

返回值

  • 如果执行成功,返回 nil
  • 如果发生错误,返回错误

Go 版本:Go 1.18+

示例

deps := testdeps.TestDeps{}

err := deps.CoordinateFuzzing(
    60*time.Second,      // timeout
    1000000,             // limit
    10*time.Second,      // minimizeTimeout
    100000,              // minimizeLimit
    8,                   // parallel
    seedEntries,         // seed
    types,               // types
    "/path/to/corpus",   // corpusDir
    "/path/to/cache",    // cacheDir
)
if err != nil {
    // 处理错误
}

I

ImportPath

func (TestDeps) ImportPath() string

作用:返回测试二进制文件的导入路径

返回值

  • 测试包的导入路径

相关变量

var ImportPath string

这个变量由生成的 main 函数在运行时设置。

示例

deps := testdeps.TestDeps{}
path := deps.ImportPath()
fmt.Println("Testing package:", path)
// 输出:Testing package: example.com/mypackage

InitRuntimeCoverage

func (TestDeps) InitRuntimeCoverage() (mode string, tearDown func(string, string) (string, error), err error)

作用:初始化运行时覆盖率数据收集

返回值

  • mode:覆盖率模式
  • tearDown:清理函数
  • err:错误(如果有)

说明

  • 当使用 -cover 标志时调用
  • 返回的清理函数用于停止覆盖率收集

示例

deps := testdeps.TestDeps{}

mode, tearDown, err := deps.InitRuntimeCoverage()
if err != nil {
    // 处理错误
}
defer tearDown("output_dir", "package_path")

M

MatchString

func (TestDeps) MatchString(pat, str string) (result bool, err error)

作用:使用正则表达式匹配字符串

参数说明

  • pat:正则表达式模式
  • str:要匹配的字符串

返回值

  • result:是否匹配成功
  • err:编译正则表达式的错误(如果有)

实现细节

  • 使用 regexp.Compile 编译模式
  • 缓存编译后的正则表达式以提高性能

示例

deps := testdeps.TestDeps{}

// 匹配测试名称
matched, err := deps.MatchString("Test.*", "TestExample")
if err != nil {
    // 处理错误
}
fmt.Println("Matched:", matched) // 输出:Matched: true

matched, err = deps.MatchString("Test.*", "BenchmarkExample")
fmt.Println("Matched:", matched) // 输出:Matched: false

ModulePath

func (TestDeps) ModulePath() string

作用:返回模块路径

返回值

  • 当前模块的路径

示例

deps := testdeps.TestDeps{}
path := deps.ModulePath()
fmt.Println("Module path:", path)

R

ReadCorpus

func (TestDeps) ReadCorpus(dir string, types []reflect.Type) ([]fuzz.CorpusEntry, error)

作用:从目录读取语料库

参数说明

  • dir:语料库目录
  • types:期望的类型

返回值

  • 语料库条目切片
  • 读取错误(如果有)

Go 版本:Go 1.18+

示例

deps := testdeps.TestDeps{}

entries, err := deps.ReadCorpus("/path/to/corpus", types)
if err != nil {
    // 处理错误
}

for _, entry := range entries {
    // 处理语料库条目
}

ResetCoverage

func (TestDeps) ResetCoverage()

作用:重置覆盖率数据

说明

  • 清除之前收集的覆盖率数据
  • 用于在多次测试运行之间重置状态

示例

deps := testdeps.TestDeps{}

// 运行一些测试
runTests()

// 重置覆盖率数据
deps.ResetCoverage()

// 重新开始收集覆盖率

RunFuzzWorker

func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error

作用:运行模糊测试工作协程

参数说明

  • fn:处理语料库条目的函数

返回值

  • 执行错误(如果有)

Go 版本:Go 1.18+

示例

deps := testdeps.TestDeps{}

err := deps.RunFuzzWorker(func(entry fuzz.CorpusEntry) error {
    // 处理语料库条目
    return nil
})
if err != nil {
    // 处理错误
}

S

SetPanicOnExit0

func (TestDeps) SetPanicOnExit0(v bool)

作用:告诉 os 包是否在 os.Exit(0) 时 panic

参数说明

  • v:是否启用 panic

说明

  • 用于测试框架检测意外的 os.Exit(0) 调用
  • 在正常测试中不应该调用 os.Exit(0)

示例

deps := testdeps.TestDeps{}

// 启用 os.Exit(0) 时的 panic
deps.SetPanicOnExit0(true)

// 现在调用 os.Exit(0) 会导致 panic

SnapshotCoverage

func (TestDeps) SnapshotCoverage()

作用:创建覆盖率数据的快照

说明

  • 用于保存当前覆盖率状态
  • 通常由测试框架自动调用

示例

deps := testdeps.TestDeps{}

// 运行一些测试
runTests()

// 创建覆盖率快照
deps.SnapshotCoverage()

StartCPUProfile

func (TestDeps) StartCPUProfile(w io.Writer) error

作用:开始 CPU 性能分析

参数说明

  • w:写入性能分析数据的 writer

返回值

  • 启动错误(如果有)

实现

  • 调用 pprof.StartCPUProfile

示例

deps := testdeps.TestDeps{}

var buf bytes.Buffer
err := deps.StartCPUProfile(&buf)
if err != nil {
    // 处理错误
}
defer deps.StopCPUProfile()

// 运行测试
runTests()

StartTestLog

func (TestDeps) StartTestLog(w io.Writer)

作用:开始测试日志记录

参数说明

  • w:写入日志的 writer

说明

  • 记录测试期间的文件系统操作
  • 用于检测测试的副作用

示例

deps := testdeps.TestDeps{}

var buf bytes.Buffer
deps.StartTestLog(&buf)

// 运行测试
runTests()

deps.StopTestLog()
fmt.Println(buf.String())

St

StopCPUProfile

func (TestDeps) StopCPUProfile()

作用:停止 CPU 性能分析

实现

  • 调用 pprof.StopCPUProfile

示例

deps := testdeps.TestDeps{}

deps.StartCPUProfile(os.Stdout)
// 运行测试
runTests()
deps.StopCPUProfile()

StopTestLog

func (TestDeps) StopTestLog() error

作用:停止测试日志记录

返回值

  • 刷新日志的错误(如果有)

示例

deps := testdeps.TestDeps{}

deps.StartTestLog(os.Stdout)
// 运行测试
runTests()

err := deps.StopTestLog()
if err != nil {
    // 处理错误
}

W

WriteProfileTo

func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error

作用:写入性能分析数据到 writer

参数说明

  • name:性能分析名称(如 “goroutine”, “heap”, “threadcreate”)
  • w:写入数据的 writer
  • debug:调试级别(0 或 1)

返回值

  • 写入错误(如果有)

实现

  • 调用 pprof.Lookup(name).WriteTo(w, debug)

示例

deps := testdeps.TestDeps{}

// 写入 goroutine 性能分析
var buf bytes.Buffer
err := deps.WriteProfileTo("goroutine", &buf, 0)
if err != nil {
    // 处理错误
}

// 写入 heap 性能分析
err = deps.WriteProfileTo("heap", os.Stdout, 1)
if err != nil {
    // 处理错误
}

变量详解

Cover

var Cover bool

作用:指示是否启用了覆盖率

说明

  • 当使用 -cover 标志时为 true
  • 由运行时(通过 testmain 中的代码)设置

ImportPath

var ImportPath string

作用:测试二进制文件的导入路径

说明

  • 由生成的 main 函数设置
  • 用于标识被测试的包

典型示例

1. 生成的测试主函数

// 这是 go test 生成的代码示例
package main

import (
    "os"
    "testing"
    "testing/internal/testdeps"
)

var tests = []testing.InternalTest{
    {"TestExample", TestExample},
}

var benchmarks = []testing.InternalBenchmark{
    {"BenchmarkExample", BenchmarkExample},
}

func main() {
    deps := testdeps.TestDeps{}
    
    // 设置导入路径
    testdeps.ImportPath = "example.com/mypackage"
    
    // 运行测试
    testing.Main(deps.MatchString, tests, benchmarks, nil)
}

2. 使用 MatchString 过滤测试

package main

import (
    "fmt"
    "testing"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 测试名称匹配
    patterns := []string{
        "Test.*",
        "Benchmark.*",
        "Example.*",
    }
    
    names := []string{
        "TestExample",
        "BenchmarkExample",
        "ExampleFunction",
    }
    
    for _, pat := range patterns {
        fmt.Printf("\nPattern: %s\n", pat)
        for _, name := range names {
            matched, _ := deps.MatchString(pat, name)
            fmt.Printf("  %s: %v\n", name, matched)
        }
    }
}

3. 性能分析示例

package main

import (
    "os"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 开始 CPU 性能分析
    f, _ := os.Create("cpu.prof")
    deps.StartCPUProfile(f)
    defer deps.StopCPUProfile()
    defer f.Close()
    
    // 运行基准测试
    runBenchmarks()
    
    // 写入内存性能分析
    memFile, _ := os.Create("mem.prof")
    deps.WriteProfileTo("heap", memFile, 0)
    memFile.Close()
}

4. 测试日志记录

package main

import (
    "bytes"
    "fmt"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    var buf bytes.Buffer
    deps.StartTestLog(&buf)
    
    // 运行一些会访问文件系统的代码
    doFileOperations()
    
    err := deps.StopTestLog()
    if err != nil {
        panic(err)
    }
    
    fmt.Println("Test log:")
    fmt.Println(buf.String())
}

5. 覆盖率数据收集

package main

import (
    "fmt"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 初始化覆盖率收集
    mode, tearDown, err := deps.InitRuntimeCoverage()
    if err != nil {
        panic(err)
    }
    defer tearDown("./coverage", "example.com/mypackage")
    
    fmt.Println("Coverage mode:", mode)
    
    // 运行测试
    runTests()
    
    // 创建快照
    deps.SnapshotCoverage()
    
    // 重置覆盖率
    deps.ResetCoverage()
}

6. 模糊测试协调

package main

import (
    "reflect"
    "testing/internal/testdeps"
    "time"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 准备模糊测试类型
    types := []reflect.Type{
        reflect.TypeOf(""),
        reflect.TypeOf([]byte{}),
    }
    
    // 协调模糊测试
    err := deps.CoordinateFuzzing(
        60*time.Second,  // timeout
        1000000,         // limit
        10*time.Second,  // minimizeTimeout
        100000,          // minimizeLimit
        8,               // parallel
        nil,             // seed
        types,           // types
        "./corpus",      // corpusDir
        "./cache",       // cacheDir
    )
    if err != nil {
        panic(err)
    }
}

7. 读取和验证语料库

package main

import (
    "fmt"
    "reflect"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 读取语料库
    types := []reflect.Type{
        reflect.TypeOf([]byte{}),
    }
    
    entries, err := deps.ReadCorpus("./corpus", types)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Read %d corpus entries\n", len(entries))
    
    // 验证语料库
    for _, entry := range entries {
        err := deps.CheckCorpus([]any{entry.Data}, types)
        if err != nil {
            fmt.Printf("Invalid entry: %v\n", err)
        }
    }
}

8. 运行模糊测试工作协程

package main

import (
    "fmt"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    
    // 运行模糊测试工作协程
    err := deps.RunFuzzWorker(func(entry fuzz.CorpusEntry) error {
        // 处理语料库条目
        fmt.Printf("Processing entry: %v\n", entry)
        return nil
    })
    if err != nil {
        panic(err)
    }
}

最佳实践

1. 让 go test 自动处理

// 不需要手动创建 TestDeps
// go test 会自动生成必要的代码

// 只需编写测试函数
func TestExample(t *testing.T) {
    // 测试代码
}

2. 理解生成的代码

// 了解 go test 生成的代码结构
// 这有助于理解测试执行流程

package main

import (
    "os"
    "testing"
    "testing/internal/testdeps"
)

var tests = []testing.InternalTest{
    {"TestName", TestName},
}

func main() {
    deps := testdeps.TestDeps{}
    testing.Main(deps.MatchString, tests, nil, nil)
}

3. 使用正确的性能分析名称

deps := testdeps.TestDeps{}

// 有效的性能分析名称
profiles := []string{
    "goroutine",    // 协程栈
    "heap",         // 堆分配
    "allocs",       // 分配历史
    "block",        // 阻塞同步
    "mutex",        // 互斥锁竞争
    "threadcreate", // 线程创建
}

for _, name := range profiles {
    deps.WriteProfileTo(name, os.Stdout, 0)
}

4. 处理覆盖率数据

deps := testdeps.TestDeps{}

// 初始化
mode, tearDown, err := deps.InitRuntimeCoverage()
if err != nil {
    // 处理错误
}

// 确保清理
defer func() {
    _, err := tearDown("output", "package")
    if err != nil {
        // 处理错误
    }
}()

与其他包配合

testing 包

import (
    "testing"
    "testing/internal/testdeps"
)

func main() {
    deps := testdeps.TestDeps{}
    testing.Main(deps.MatchString, tests, benchmarks, examples)
}

runtime/pprof 包

import (
    "runtime/pprof"
    "testing/internal/testdeps"
)

func Profile() {
    deps := testdeps.TestDeps{}
    deps.StartCPUProfile(os.Stdout)
    // pprof.StartCPUProfile 被内部调用
}

regexp 包

import (
    "regexp"
    "testing/internal/testdeps"
)

func Match() {
    deps := testdeps.TestDeps{}
    // 内部使用 regexp.Compile
    matched, _ := deps.MatchString("Test.*", "TestExample")
}

注意事项

1. 内部包限制

// 不推荐直接使用内部包
import "testing/internal/testdeps"

// 应该使用 go test 命令
// go test 会自动处理所有依赖

2. API 不稳定性

// 内部包的 API 可能会在任何 Go 版本中更改
// 不要依赖特定的行为或签名

// 好的做法:使用 go test
// go test -v -cover -cpu=4

// 不好的做法:手动调用内部包
deps := testdeps.TestDeps{}
deps.InitRuntimeCoverage()

3. 覆盖率模式

// 覆盖率数据只在 -cover 标志下收集
// Cover 变量由运行时设置

if testdeps.Cover {
    // 启用了覆盖率
} else {
    // 未启用覆盖率
}

4. 导入路径设置

// ImportPath 必须由生成的 main 函数设置
testdeps.ImportPath = "example.com/mypackage"

// 不要手动修改已设置的值

5. 性能分析资源

// 确保正确清理性能分析资源
deps.StartCPUProfile(f)
defer deps.StopCPUProfile()
defer f.Close()

// 不要忘记 StopCPUProfile

快速参考

TestDeps 方法速查表

方法Go 版本作用
CheckCorpus1.18+验证语料库数据
CoordinateFuzzing1.18+协调模糊测试
ImportPath-返回导入路径
InitRuntimeCoverage-初始化覆盖率
MatchString-正则表达式匹配
ModulePath-返回模块路径
ReadCorpus1.18+读取语料库
ResetCoverage-重置覆盖率
RunFuzzWorker1.18+运行模糊测试
SetPanicOnExit0-设置 Exit0 panic
SnapshotCoverage-覆盖率快照
StartCPUProfile-开始 CPU 分析
StartTestLog-开始测试日志
StopCPUProfile-停止 CPU 分析
StopTestLog-停止测试日志
WriteProfileTo-写入性能分析

变量速查表

变量类型作用
Coverbool覆盖率启用标志
ImportPathstring测试二进制导入路径

性能分析名称

名称说明
goroutine协程栈信息
heap堆内存分配
allocs分配历史
block阻塞同步原语
mutex互斥锁竞争
threadcreate线程创建

常见模式

// 测试执行模式
deps := testdeps.TestDeps{}
testing.Main(deps.MatchString, tests, benchmarks, examples)

// 性能分析模式
deps.StartCPUProfile(w)
defer deps.StopCPUProfile()

// 覆盖率模式
mode, tearDown, _ := deps.InitRuntimeCoverage()
defer tearDown(output, pkg)

总结

testing/internal 包目录包含 testing 包的内部实现支持:

核心包

  • testdeps:测试执行依赖

主要功能

  • 测试名称匹配(MatchString
  • 性能分析(StartCPUProfileWriteProfileTo
  • 测试日志(StartTestLogStopTestLog
  • 覆盖率支持(InitRuntimeCoverageSnapshotCoverage
  • 模糊测试(CoordinateFuzzingRunFuzzWorker

设计目标

  1. 避免 testing 包的直接依赖
  2. 支持高级测试功能
  3. go test 自动管理

使用建议

  1. go test 自动处理所有内部包
  2. 理解生成的代码结构
  3. 不要直接依赖内部包 API
  4. 使用标准的 go test 命令和标志

典型用法

// go test 生成的代码
package main

import (
    "testing"
    "testing/internal/testdeps"
)

var tests = []testing.InternalTest{
    {"TestExample", TestExample},
}

func main() {
    deps := testdeps.TestDeps{}
    testing.Main(deps.MatchString, tests, nil, nil)
}

通过 testing/internal 包,Go 测试框架能够支持高级功能,同时保持 testing 包的简洁性和可测试性。