【Go语言学习系列20】单元测试基础

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第20篇,当前位于第二阶段(基础巩固篇)

🚀 第二阶段:基础巩固篇
  1. 13-包管理深入理解
  2. 14-标准库探索(一):io与文件操作
  3. 15-标准库探索(二):字符串处理
  4. 16-标准库探索(三):时间与日期
  5. 17-标准库探索(四):JSON处理
  6. 18-标准库探索(五):HTTP客户端
  7. 19-标准库探索(六):HTTP服务器
  8. 20-单元测试基础 👈 当前位置
  9. 21-基准测试与性能剖析入门
  10. 22-反射机制基础
  11. 23-Go中的面向对象编程
  12. 24-函数式编程在Go中的应用
  13. 25-context包详解
  14. 26-依赖注入与控制反转
  15. 27-第二阶段项目实战:RESTful API服务

📚 查看完整Go语言学习系列导航

📖 文章导读

在本文中,您将了解:

  • Go语言测试的基本概念和工作原理
  • 编写有效单元测试的最佳实践
  • 表格驱动测试与测试辅助函数
  • 模拟外部依赖与测试替身
  • 测试覆盖率分析与持续集成

单元测试是确保代码质量和可靠性的重要工具,而Go语言内置的testing包提供了简单而强大的测试支持。本文将从基础开始,深入讲解Go语言测试的各个方面,帮助您构建更健壮、更可维护的Go程序。

1. Go测试的基本概念

1.1 什么是单元测试

单元测试是针对程序中最小可测试单元(通常是函数或方法)的测试。在Go中,这通常指对特定函数或方法的输入和输出进行验证,确保它们按预期工作。单元测试的目标是:

  • 验证代码的正确性
  • 防止回归(确保修改不会破坏现有功能)
  • 促进代码重构
  • 提供文档和示例

1.2 Go测试文件命名约定

Go的测试文件遵循以下命名约定:

  • 测试文件以_test.go结尾
  • 测试文件通常与被测代码在同一包中
  • 测试函数名以Test开头

例如,如果你有一个名为calculator.go的文件,对应的测试文件应命名为calculator_test.go

1.3 测试函数结构

Go测试函数的基本结构如下:

func TestXxx(t *testing.T) {
   
   
    // 测试代码
}

要点:

  • 测试函数必须以Test开头,后跟大写字母开头的单词
  • 测试函数必须接受*testing.T类型的参数
  • 测试函数没有返回值

2. 编写基本测试

让我们通过具体例子学习如何编写基本测试。首先,创建一个简单的计算器包:

// calculator.go
package calculator

// Add 返回两个整数的和
func Add(a, b int) int {
   
   
    return a + b
}

// Subtract 返回两个整数的差
func Subtract(a, b int) int {
   
   
    return a - b
}

// Multiply 返回两个整数的乘积
func Multiply(a, b int) int {
   
   
    return a * b
}

// Divide 返回两个整数的商,如果除数为0则panic
func Divide(a, b int) int {
   
   
    if b == 0 {
   
   
        panic("除数不能为0")
    }
    return a / b
}

现在,为这些函数编写测试:

// calculator_test.go
package calculator

import (
    "testing"
)

func TestAdd(t *testing.T) {
   
   
    result := Add(3, 2)
    expected := 5
    
    if result != expected {
   
   
        t.Errorf("Add(3, 2) = %d; 期望 %d", result, expected)
    }
}

func TestSubtract(t *testing.T) {
   
   
    result := Subtract(5, 2)
    expected := 3
    
    if result != expected {
   
   
        t.Errorf("Subtract(5, 2) = %d; 期望 %d", result, expected)
    }
}

func TestMultiply(t *testing.T) {
   
   
    result := Multiply(4, 3)
    expected := 12
    
    if result != expected {
   
   
        t.Errorf("Multiply(4, 3) = %d; 期望 %d", result, expected)
    }
}

func TestDivide(t *testing.T) {
   
   
    result := Divide(6, 2)
    expected := 3
    
    if result != expected {
   
   
        t.Errorf("Divide(6, 2) = %d; 期望 %d", result, expected)
    }
}

func TestDivideByZero(t *testing.T) {
   
   
    // 使用匿名函数封装可能panic的操作
    defer func() {
   
   
        if r := recover(); r == nil {
   
   
            t.Errorf("除以零应该引发panic")
        }
    }()
    
    Divide(6, 0)
}

2.1 运行测试

可以使用go test命令运行测试:

go test                # 运行当前包中的所有测试
go test -v             # 详细模式,显示每个测试函数的结果
go test ./...          # 运行当前目录及其子目录中的所有测试
go test -run TestAdd   # 只运行名称匹配"TestAdd"的测试

2.2 理解测试输出

详细模式下的输出示例:

=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestSubtract
--- PASS: TestSubtract (0.00s)
=== RUN   TestMultiply
--- PASS: TestMultiply (0.00s)
=== RUN   TestDivide
--- PASS: TestDivide (0.00s)
=== RUN   TestDivideByZero
--- PASS: TestDivideByZero (0.00s)
PASS
ok      github.com/yourusername/calculator   0.002s

如果测试失败,输出将包含错误信息:

=== RUN   TestAdd
--- FAIL: TestAdd (0.00s)
    calculator_test.go:14: Add(3, 2) = 6; 期望 5
FAIL
exit status 1
FAIL    github.com/yourusername/calculator   0.002s

3. 表格驱动测试

Go社区推崇"表格驱动测试"的模式,这种模式通过创建测试用例表格,使测试更加简洁和可维护。

func TestAdd_TableDriven(t *testing.T) {
   
   
    // 定义测试用例表
    tests := []struct {
   
   
        name     string  // 测试名称
        a, b     int     // 输入参数
        expected int     // 期望结果
    }{
   
   
        {
   
   "正数相加", 3, 2, 5},
        {
   
   "负数相加", -3, -2, -5},
        {
   
   "正负相加", 3, -2, 1},
        {
   
   "零值处理", 0, 0, 0},
        {
   
   "大数相加", 1000000, 1000000, 2000000},
    }
    
    // 遍历测试用例表
    for _, tt := range tests {
   
   
        t.Run(tt.name, func(t *testing.T) {
   
   
            result := Add(tt.a, tt.b)
            if result != tt.expected {
   
   
                t.Errorf("Add(%d, %d) = %d; 期望 %d", 
                        tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

表格驱动测试的主要优势:

  • 易于添加新的测试用例
  • 代码重复减少
  • 测试报告更有信息量
  • 更好的测试覆盖范围
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值