广告

Go语言测试入门:testing 包的使用详解与实战案例

1. Go 测试基础与 testing 包概览

1.1 testing 包的定位与核心对象

本文聚焦 Go语言测试入门:testing 包的使用详解与实战案例 的核心要点。testing 包 是 Go 标准库中的测试框架,负责组织、执行和报告测试。核心对象包括 testing.Ttesting.Btesting.M,分别用于单元测试、基准测试和入口控制。

通过 TestXxx 命名的函数会被自动识别为测试用例,BenchmarkXxx 用于基准测试,而 TestMain(m *testing.M) 提供全局初始化与清理的入口。

测试运行器会汇总测试结果,t.Failt.Fatalft.Skip 等 API 提供不同的失败策略和跳过机制。

package example

import "testing"

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

func TestSum(t *testing.T) {
    if Sum(2, 3) != 5 {
        t.Fatalf("Sum(2,3) = %d; want 5", Sum(2, 3))
    }
}

1.2 测试与包结构的组织原则

测试文件命名以 _test.go 结尾,文件内容放在与被测试代码同包名的目录下。包名可以是 xxxxxx_test,后者有利于实现黑盒测试和更严格的 API 粒度。

在实际项目中,保持测试代码与生产代码的分离是关键,目录结构应与模块结构对齐,方便 CI 运行。多个测试文件可以共享辅助函数或测试数据。

1.3 测试文件与示例代码组织

// 文件:math/math.go
package math

func Add(a, b int) int { return a + b }
// 文件:math/math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    if got := Add(2, 3); got != 5 {
        t.Fatalf("Add(2,3) = %d; want 5", got)
    }
}

2. 编写你的第一个测试用例

2.1 测试文件结构与初步示例

要开始 Go 测试,首先确认你的项目包含测试文件以 _test.go 结尾。测试文件可以与被测试的源码在同一包中,也可以在独立的测试包中进行黑盒测试。

下面给出一个简单示例,演示如何为一个加法函数编写基本测试。测试函数以 Test 前缀命名,并接收 *testing.T 参数。

// 文件:math/add.go
package math

func Add(a, b int) int { return a + b }
// 文件:math/add_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    if got := Add(2, 3); got != 5 {
        t.Fatalf("Add(2,3) = %d; want 5", got)
    }
}

2.2 使用 t.Fatalf 提供清晰的断言信息

在断言失败时,t.Fatalf 可以输出详细信息并立即停止当前测试,确保失败原因可追溯。正确的断言信息有助于定位逻辑错误和边界条件。

你也可以使用 t.Errorf 输出错误但继续执行其他子测试,这在表格驱动测试中尤其有用。

package math

import "testing"

func TestAddEdge(t *testing.T) {
    cases := []struct{ a, b, want int }{
        {0, 0, 0},
        {1, -1, 0},
        {100, 200, 300},
    }
    for _, c := range cases {
        if got := Add(c.a, c.b); got != c.want {
            t.Errorf("Add(%d,%d) = %d; want %d", c.a, c.b, got, c.want)
        }
    }
}

3. 使用 testing 包的高级特性

3.1 子测试与 t.Run 的用法

通过 t.Run 可以在同一个测试函数中创建多个子测试,便于隔离测试用例、单独统计失败情况,并提升并行测试的可控性。

使用子测试时,t.Parallel 可以促进并行执行,但需要避免并发访问共享数据。

package math

import "testing"

func TestDivide(t *testing.T) {
    t.Run("normal", func(t *testing.T) {
        if 10/2 != 5 {
            t.Fatalf("unexpected result")
        }
    })
    t.Run("division by zero", func(t *testing.T) {
        defer func() {
            if r := recover(); r == nil {
                t.Fatalf("expected panic on divide by zero")
            }
        }()
        _ = 1 / 0
    })
}

3.2 表格驱动测试的实践

表格驱动测试将输入输出对整理成数据结构,循环遍历进行断言,提高代码可维护性和覆盖率

下面示例展示了一个典型的表格驱动测试结构。

package math

import "testing"

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

func TestAdd_TableDriven(t *testing.T) {
    cases := []struct {
        a, b, want int
    }{
        {1, 2, 3},
        {2, 2, 4},
        {0, 0, 0},
    }
    for _, c := range cases {
        if got := Add(c.a, c.b); got != c.want {
            t.Fatalf("Add(%d,%d) = %d; want %d", c.a, c.b, got, c.want)
        }
    }
}

4. 实战案例:结合基准测试与覆盖率

4.1 基准测试示例

基准测试(Benchmark)用于评估代码的性能,并帮助定位瓶颈。Go 的基准测试以 BenchmarkXxx 命名,参数为 *testing.B

在基准测试中,循环次数由系统根据 b.N 自动调整,确保测量的可重复性与统计稳定性。

package math

import "testing"

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

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

4.2 测试覆盖率与持续集成集成

通过覆盖率可以了解代码哪些部分被测试覆盖,go test -cover 命令输出覆盖率百分比,结合持续集成工具可以持续提升覆盖率。

go test ./... -cover
广告

后端开发标签