常识小站
第二套高阶模板 · 更大气的阅读体验

Go语言单元测试实例:从零开始写可靠的代码

发布时间:2025-12-13 17:43:31 阅读:246 次
{"title":"Go语言单元测试实例:从零开始写可靠的代码","content":"

你有没有遇到过改了一行代码,结果整个程序崩了的情况?有时候一个小功能上线,没测全,线上报警一堆。这种情况在团队协作中尤其常见。为了解决这个问题,写单元测试就成了开发中绕不开的一环。在 Go 语言里,写单元测试不仅简单,而且几乎不用额外引入框架。

\n\n

Go 的测试机制长什么样

\n

Go 自带 testing 包,只要按照约定写测试文件,就能直接运行。测试文件通常和源码放在同一个目录下,名字是原文件名加上 _test.go 后缀。比如你有一个 calculator.go 文件,那测试文件就叫 calculator_test.go

\n\n

一个简单的加法测试例子

\n

假设我们写了一个做加法的函数:

\n
package main\n\nfunc Add(a, b int) int {\n    return a + b\n}\n
\n\n

接下来,在同一目录下创建 calculator_test.go 文件:

\n
package main\n\nimport (\n    "testing"\n)\n\nfunc TestAdd(t *testing.T) {\n    result := Add(2, 3)\n    expected := 5\n    if result != expected {\n        t.Errorf("期望 %d,但得到了 %d", expected, result)\n    }\n}\n
\n\n

保存后,在终端执行:

\n
go test\n
\n\n

如果输出显示 OK,说明测试通过。你可以试试把期望值改成 6,再跑一次,就会看到错误提示。

\n\n

表驱动测试让用例更清晰

\n

实际项目中,一个函数往往要测多种输入情况。一个个写 t.Run() 太麻烦。Go 社区常用“表驱动测试”(table-driven test)来组织多个用例。

\n\n

还是拿 Add 函数举例,现在我们想测几组不同的输入:

\n
func TestAddTable(t *testing.T) {\n    tests := []struct {\n        name     string\n        a, b     int\n        expected int\n    }{\n        {"正数相加", 2, 3, 5},\n        {"负数相加", -2, -3, -5},\n        {"一正一负", 5, -3, 2},\n        {"其中一个为零", 0, 7, 7},\n    }\n\n    for _, tt := range tests {\n        t.Run(tt.name, func(t *testing.T) {\n            result := Add(tt.a, tt.b)\n            if result != tt.expected {\n                t.Errorf("期望 %d,但得到了 %d", tt.expected, result)\n            }\n        })\n    }\n}\n
\n\n

这样写的好处是,新增用例只需要往切片里加一行,结构清晰,维护方便。而且每个子测试都有独立名字,出错时能快速定位是哪个场景没通过。

\n\n

测试覆盖率不是越高越好,但要看关键路径

\n

很多人追求 100% 测试覆盖率,其实没必要。像一些简单的 getter/setter 或明显不会出错的逻辑,可以适当放过。真正该覆盖的是核心业务逻辑、边界条件和错误处理。

\n\n

比如一个除法函数,除了正常计算,更要测“除以零”的情况:

\n
func Divide(a, b float64) (float64, error) {\n    if b == 0 {\n        return 0, fmt.Errorf("不能除以零")\n    }\n    return a / b, nil\n}\n
\n\n

对应的测试可以这样写:

\n
func TestDivide(t *testing.T) {\n    t.Run("正常除法", func(t *testing.T) {\n        result, err := Divide(10, 2)\n        if err != nil {\n            t.Fatal("不应该出错")\n        }\n        if result != 5 {\n            t.Errorf("期望 5,得到 %.1f", result)\n        }\n    })\n\n    t.Run("除以零", func(t *testing.T) {\n        _, err := Divide(10, 0)\n        if err == nil {\n            t.Fatal("应该返回错误")\n        }\n        if err.Error() != "不能除以零" {\n            t.Errorf("错误信息不符:%s", err)\n        }\n    })\n}\n
\n\n

这类测试虽然看起来琐碎,但在项目迭代中特别有用。别人改代码时,一旦不小心破坏了原有逻辑,测试会立刻报错。

\n\n

日常开发中的小建议

\n

写单元测试不一定要等到功能做完才开始。TDD(测试驱动开发)提倡先写测试,再实现功能。哪怕你不完全照做,也可以养成“写完函数顺手补个测试”的习惯。

\n\n

比如你在写一个用户注册逻辑,刚写完邮箱格式校验函数,马上写几个测试用例:空字符串、普通邮箱、非法格式(如 missing@dot)。等以后重构时,这些测试就是你的安全网。

\n\n

Go 的测试机制足够轻量,不需要复杂的配置。每天花十分钟写几个测试,可能就避免了半夜被线上问题叫醒。”,"seo_title":"Go语言单元测试实例详解 - 常识小站网络技术栏目","seo_description":"通过实际代码示例讲解Go语言单元测试的写法,包括基础测试、表驱动测试和错误处理,帮助开发者写出更可靠的程序。","keywords":"Go语言,单元测试,Go测试实例,Go test,表驱动测试,Golang测试"}