小工具的测试策略:只测那几处容易错的地方

举报
yd_271078175 发表于 2026/11/03 09:41:06 2026/11/03
【摘要】 给一个几百行的命令行小工具写测试,最容易走两个极端:一是不写,靠手动跑;二是追求覆盖率,给每个函数都套一层。

给一个几百行的命令行小工具写测试,最容易走两个极端:一是不写,靠手动跑;二是追求覆盖率,给每个函数都套一层。

两种都不划算。小工具的错误高度集中:真正会出问题的就那么几个纯函数,其余的失败大多是网络、权限、配置,测也测不出来。这篇说说我会测什么。

一、先分清三类代码

写之前我会把代码分成三堆:

  • 纯函数:输入确定、输出确定、不碰网络和磁盘。比如时间截断、参数解析、字符串拼装。
  • 编排逻辑:串起多个步骤,决定先做什么后做什么。
  • 外部交互:HTTP 请求、文件读写、子进程。

第三类最难测也最少出逻辑错误,它出错往往是环境问题,靠日志和重试解决,不值得写单元测试。第一类正好相反:逻辑全在里面,错了还很难从日志看出来。

所以测试写在第一类。第二类靠一次端到端手动验证。

二、值得测的四个典型

时间相关的函数。 跨零点、跨时区、闰年,这几个边界人脑模拟起来容易错:

func TestDayOf(t *testing.T) {
    loc, _ := time.LoadLocation("Asia/Shanghai")
    a := time.Date(2026, 10, 31, 23, 59, 59, 0, loc)
    b := time.Date(2026, 11, 1, 0, 0, 1, 0, loc)
    if dayOf(a).Equal(dayOf(b)) {
        t.Fatal("跨零点被判断成同一天")
    }
}

解析和格式化。 配置字段多了就有「没填」和「填了空」的区别,这类分支不测很容易漏。

状态判断。 「今天做过了吗」「这次算成功吗」——业务规则写错的成本最高,改起来也最便宜。

补跑、重试这类计数逻辑。 上限算不算、从 0 开始还是从 1 开始,都是典型的差一错误。

三、时间要能注入,不然测不了

上面那个 dayOf 能测,是因为它接收 time.Time 参数。如果函数内部直接调 time.Now(),就只能测「现在」这一种情况。

改造成本很小:

// 不好测
func shouldRun() bool {
    return time.Now().Hour() == 9
}

// 好测
func shouldRunAt(now time.Time) bool {
    return now.Hour() == 9
}

外面调的时候传 time.Now() 进去。多一个参数,换来的可测性是值得的。

四、别为外部接口造假实现

给 HTTP 客户端写 mock 是小工具里性价比最低的事。要造的东西太多:状态码、响应体、超时、重试,造完一圈发现测的是 mock 的行为,不是程序的行为。

真需要验证「拿到 500 会不会重试」,用一个 httptest.Server 起个真服务,几行就够:

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusInternalServerError)
}))
defer srv.Close()

它走的是真的 HTTP 栈,测出来的结论可信。

五、测试要能一次跑完

小工具的测试应该几秒钟跑完,不依赖网络和登录态。一旦某个测试需要真实 Cookie,整个 go test 就变成了手工操作,很快没人跑。

判断标准:go test ./... 在任何一台干净机器上都能通过。做不到的,那部分就别写成测试,写进文档说明怎么手动验证。

六、小结

小工具的测试不是用来证明「代码正确」,是用来防止「改一处错另一处」。所以覆盖范围窄一点没关系,关键是那几处一旦写错就会静默出问题的逻辑。

我的做法:纯函数全测,外部交互不测,端到端手动跑一遍。几百行的工具,测试文件通常也就一百行出头。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。