Go语言单元测试实例:从零开始写可靠的代码(详细解析)

为什么需要单元测试

开发一个功能时,比如计算订单总价,改着改着可能把原来的逻辑搞坏了。手动点来点去验证太麻烦,还容易漏。这时候单元测试就派上用场了——它像个小机器人,每次代码一动,它就自动跑一遍检查,哪里出问题马上告诉你。

Go 语言里写单元测试特别简单,标准库自带支持,不用额外装工具。

测试文件怎么写

假设你有个文件叫 calculator.go,里面有个函数用来加两个数:

package main

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

那对应的测试文件就是 calculator_test.go,名字要以 _test.go 结尾:

package main

import (
    "testing"
)

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

写完后,在终端运行 go test,就能看到结果。如果输出 OK,说明通过;如果有错,会提示哪一行不符合预期。

测试多个场景

一个函数往往有多种输入情况。比如减法函数,不仅要测正数,还要测负数、零:

func Subtract(a, b int) int {
    return a - b
}

func TestSubtract(t *testing.T) {
    cases := []struct {
        a, b     int
        expected int
    }{
        {10, 5, 5},
        {5, 10, -5},
        {0, 0, 0},
        {-1, -1, 0},
    }

    for _, c := range cases {
        result := Subtract(c.a, c.b)
        if result != c.expected {
            t.Errorf("Subtract(%d, %d) = %d, 期望 %d", c.a, c.b, result, c.expected)
        }
    }
}

这种写法把多个测试用例集中在一起,以后加新情况也方便。

测试覆盖率怎么看

写完测试,可以看看这些测试到底覆盖了多少代码。运行命令:

go test -cover

会输出类似 coverage: 85.7% of statements 的信息。想看更详细的报告,可以生成 HTML:

go test -coverprofile=cover.out
go tool cover -html=cover.out

浏览器会打开一个页面,绿色是被测到的代码,红色是没覆盖到的部分。你可以盯着红的地方补测试,让项目更稳。

实际项目中的小技巧

在 Web 服务中,经常要测处理 HTTP 请求的函数。可以用 net/http/httptest 模拟请求:

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func HelloHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, World!"))
}

func TestHelloHandler(t *testing.T) {
    req := httptest.NewRequest("GET", "/hello", nil)
    w := httptest.NewRecorder()

    HelloHandler(w, req)

    resp := w.Result()
    if resp.StatusCode != http.StatusOK {
        t.Errorf("状态码不是 200")
    }

    body := w.Body.String()
    if body != "Hello, World!" {
        t.Errorf("响应体不符: %s", body)
    }
}

这样不用真正启动服务器也能验证接口行为,速度快,适合集成进自动化流程。

团队协作时,很多人提交代码前都会跑一遍测试。CI 系统(比如 GitHub Actions)也能自动执行 go test,通不过就不让合并,避免把问题带进主干。