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

相关推荐
研究司马懿1 天前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
梦想很大很大1 天前
使用 Go + Gin + Fx 构建工程化后端服务模板(gin-app 实践)
前端·后端·go
lekami_兰2 天前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘2 天前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤2 天前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt112 天前
AI DDD重构实践
go
Grassto4 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto6 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室7 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题7 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo