Go 测试

1. 测试文件的组织

在 Go 中,测试文件通常与被测试的源文件位于同一包内,并遵循以下命名规范:

  • 源文件xxx.go
  • 测试文件xxx_test.go

测试文件的命名必须以 _test.go 结尾,这样 Go 工具链才能识别并将其作为测试文件处理。

2. 测试函数的签名

测试函数必须满足以下签名:

go 复制代码
func TestXxx(t *testing.T) { ... }
  • 函数名以 Test 开头,后面跟被测试的函数或功能名称(通常首字母大写)。
  • 接受一个参数 *testing.T,用于报告测试失败和日志信息。

二、编写测试函数

1. 基本示例

假设有一个简单的加法函数 Add,位于 math.go 文件中:

go 复制代码
// math.go
package mathutil

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

对应的测试文件 math_test.go 可以这样编写:

go 复制代码
// math_test.go
package mathutil

import (
    "testing"
)

func TestAdd(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }

    for _, tt := range tests {
        result := Add(tt.a, tt.b)
        if result != tt.expected {
            t.Errorf("Add(%d, %d) = %d; expected %d", tt.a, tt.b, result, tt.expected)
        }
    }
}

2. 使用子测试

Go 1.7 引入了子测试(subtests),使得在同一个测试函数中运行多个相关测试更加方便:

go 复制代码
func TestAdd(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }

    for _, tt := range tests {
        t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; expected %d", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

3. 使用表驱动测试

表驱动测试是一种常见的测试模式,通过定义一组输入和预期输出来测试函数的行为:

go 复制代码
func TestMultiply(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {2, 3, 6},
        {0, 5, 0},
        {-2, 4, -8},
    }

    for _, tt := range tests {
        t.Run(fmt.Sprintf("%d*%d", tt.a, tt.b), func(t *testing.T) {
            result := Multiply(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Multiply(%d, %d) = %d; expected %d", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

三、运行测试

1. 使用 go test 命令

在包含测试文件的包目录下,运行以下命令即可执行所有测试:

bash 复制代码
go test

2. 运行特定测试

要运行特定的测试函数,可以使用 -run 参数,支持正则表达式:

arduino 复制代码
go test -run TestAdd

3. 查看详细输出

使用 -v 标志可以查看每个测试函数的详细输出:

bash 复制代码
go test -v

4. 并行运行测试

使用 t.Parallel() 可以让测试函数并行执行,提高测试效率:

scss 复制代码
func TestSomething(t *testing.T) {
    t.Parallel()
    // 测试代码
}

运行测试时,可以使用 -parallel 指定并行执行的测试数量:

bash 复制代码
go test -parallel 4

5.测试覆盖率

  • 使用 go test -cover 查看测试覆盖率。

  • 生成覆盖率文件:

    ini 复制代码
    go test -coverprofile=coverage.out 
    go tool cover -html=coverage.out 

示例:完整的测试流程

以下是一个综合示例,展示如何编写、运行和检查测试覆盖率。

1. 源代码

go 复制代码
// mathutil/math.go
package mathutil

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

func Multiply(a, b int) int {
    return a * b
}

2. 测试代码

go 复制代码
// mathutil/math_test.go
package mathutil

import (
    "testing"
)

func TestAdd(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }

    for _, tt := range tests {
        t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; expected %d", tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

3. 运行测试和覆盖率

ini 复制代码
go test -v -coverprofile=coverage.out
go tool cover -html=coverage.out

Go 提供了强大而简洁的测试工具,使得编写和维护测试变得非常方便。通过合理组织测试文件、编写可测试的代码、以及定期检查测试覆盖率,开发者可以确保代码的质量和可靠性。结合持续集成流程,自动化测试更是保障项目稳定性的重要手段。

相关推荐
唐青枫11 小时前
别把泛型写复杂了:Go generic 从类型参数到实战封装
go
GetcharZp1 天前
告别OOM!用Go+libvips实现30000×50000超大图片的流式瓦片服务
后端·go
妙码生花4 天前
从 PHP 到 AI + Golang,程序员自救转型手记(八):设计管理员模型、热重载配置
前端·后端·go
tyung5 天前
Go 手写 Wait-Free MPSC 无界队列:SwapPointer 实现多生产者无锁入队
后端·go
陈明勇5 天前
Go 1.26 新特性回顾:语言增强、工具升级与 Green Tea GC 默认启用
后端·go
妙码生花6 天前
从 PHP 到 AI + Golang,程序员自救转型手记(二):目录结构、初始化 GIT、设计并开发配置系统
前端·后端·go
leeyi6 天前
Deer-Go:字节 Deer-Flow 的 Go 移植,深度研究 Agent 全拆解
go·aigc·agent
Bolt7 天前
TypeScript 7.0 来了:当 tsc 用 Go 重写之后
javascript·typescript·go
Go_error7 天前
Datatypes:Go 轻松支持数据库JSON类型
后端·go
任沫8 天前
Agent之Function Call
javascript·人工智能·go