Go 语言入门系列(六):工程实践——测试、泛型与项目组织
本篇目标:从"会写代码"走向"能维护项目"。前五篇让你能写出功能正确的程序,这一篇补上让它可靠、可扩展、可交付的部分。
一、测试
Go 把测试做进了工具链,写测试几乎零成本。测试文件以 _test.go 结尾,与被测代码同包:
// calc.go
package calc
func Add(a, b int) int { return a + b }
// calc_test.go
package calc
import "testing"
func TestAdd(t *testing.T) {
if got := Add(1, 2); got != 3 {
t.Errorf("Add(1,2) = %d, 期望 3", got)
}
}
运行 go test ./...。表驱动测试是 Go 的惯用写法,一个函数覆盖多组用例:
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b, want int
}{
{"正数", 1, 2, 3},
{"负数", -1, 1, 0},
{"零", 0, 0, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := Add(c.a, c.b); got != c.want {
t.Errorf("got %d, want %d", got, c.want)
}
})
}
}
t.Run 起子测试,失败时能精确定位是哪组。覆盖率用 go test -cover,基准测试用 Benchmark 前缀的函数配合 go test -bench .。
二、泛型入门
Go 1.18 引入泛型。典型场景:写一个对多种类型都适用的函数,而不牺牲类型安全:
func Sum[T ~int | ~float64](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
Sum([]int{1, 2, 3}) // 6
Sum([]float64{1.5, 2.5}) // 4.0
[T ~int | ~float64]是类型参数和约束,~表示"底层类型为 int/float64 的自定义类型也算"- 常用约束:
comparable(可比较,能作 map 键)、any(任意类型) - 何时用:容器、算法这类真正与类型无关的逻辑。业务代码优先用接口,泛型别滥用——它会增加阅读成本。
三、包组织
- 按职责分目录,而非按层(不搞
models/、services/这种空壳分层,而是user/、order/各自封装) internal/目录:放在里面的包只能被本模块引用,是封装边界的利器
project/
├── go.mod
├── main.go
├── internal/
│ └── store/ // 外部模块无法导入
└── api/
- 避免循环依赖:包 A 导入 B,B 就不能导入 A。出现循环说明职责划分有问题,通常靠抽出公共包或定义接口解决。
- 包名简短小写,不用下划线,不叫
util、common这种含义模糊的名字。
四、依赖管理
$ go get github.com/gin-gonic/gin@v1.9.0 # 添加依赖
$ go mod tidy # 清理无用依赖、补全缺失
$ go list -m all # 查看全部依赖
版本遵循语义化:v1.2.3(主.次.补丁)。主版本升级到 v2 时导入路径要带 /v2,这是 Go 的模块版本规则。
五、标准库速览
日常高频的几个包,先混个脸熟:
fmt:格式化输出strings:Contains、Split、ReplaceAll、TrimSpacestrconv:字符串与数字互转,Atoi、Itoa、ParseFloattime:Now、Duration、Format(用2006-01-02 15:04:05这个魔法参考时间)encoding/json:Marshal/Unmarshal,结构体字段用 tag 控制:json:"name"os:文件与进程操作,os.ReadFile、os.Args
六、代码风格
- 提交前跑
go fmt ./...和go vet ./... - 更严格的静态检查用
staticcheck - 命名:导出用大写开头,短变量名(
i、n、err),包名不用驼峰 - 错误字符串不大写开头、不以标点结尾(
errors.New("file not found")) - 读一遍
Effective Go,能写之后纠正习惯最有效
七、小结与下一步
_test.go+go test,表驱动 +t.Run是标配- 泛型用于类型无关的容器/算法,约束用
~、comparable - 按职责分包,
internal/做封装边界,避免循环依赖 go get/go mod tidy管依赖,主版本 v2+ 路径带/v2- 风格靠
fmt、vet、staticcheck保证
练习:把前几篇的通讯录项目整理成规范模块——internal/ 放核心逻辑,补一套表驱动测试,跑通 go test -cover ./...,再用 go vet 和 staticcheck 检查一遍。
下一步方向:做 Web 服务(net/http 或 Gin)、写 CLI 工具、读标准库源码(sync、context 的实现很值得看)。
六篇到此结束。回顾这条路径:工具链 → 语法 → 数据 → 抽象与错误 → 并发 → 工程化。每一步都建立在前一步之上,走完这套,你已经具备了用 Go 写真实项目的基础。接下来靠项目和源码继续打磨。

浙公网安备 33010602011771号