在 Go 中编写单元测试(尤其是表格驱动测试)时,向标准全局 log 包写入日志的函数很快就会让你的终端变得杂乱不堪。
如果你有十几个测试用例,而只有一个失败了,你仍然不得不翻阅所有成功用例的日志输出才能找到错误。这种噪音会拖慢调试速度,并使测试输出难以阅读。
让我们来看一个常见场景,以及如何通过捕获日志并仅在测试失败时打印它们来解决这个问题。
假设我们有一个简单的函数,它会记录其输入和最终结果:
package add
import (
"log"
)
func add(x, y int) int {
log.Printf("add %d %d", x, y)
res := x + y
log.Printf("add %d %d = %d", x, y, res)
return res
}
现在,我们编写一个包含三个用例的标准表格驱动单元测试(其中最后一个用例是故意设计为失败的):
package add
import (
"testing"
)
func TestAdd(t *testing.T) {
inputs := []struct {
x int
y int
want int
}{
{x: 1, y: 2, want: 3},
{x: 3, y: 2, want: 5},
{x: 4, y: 5, want: 0}, // 故意失败的用例
}
for _, in := range inputs {
got := add(in.x, in.y)
if in.want != got {
t.Errorf("add %d %d got %d want %d", in.x, in.y, got, in.want)
}
}
}
当我们运行 go test 时,输出会先包含所有通过的输入的日志,最后才向我们展示失败信息:
$ go test
2020/09/05 09:01:25 add 1 2
2020/09/05 09:01:25 add 1 2 = 3
2020/09/05 09:01:25 add 3 2
2020/09/05 09:01:25 add 3 2 = 5
2020/09/05 09:01:25 add 4 5
2020/09/05 09:01:25 add 4 5 = 9
--- FAIL: TestAdd (0.00s)
add_test.go:28: add 4 5 got 9 want 0
FAIL
exit status 1
如果只看到失败输入所生成的日志,效果会好得多。我们可以通过向函数传入一个日志记录器(依赖注入),并在测试期间将日志写入临时缓冲区来实现这一点。
首先,让我们更新函数以接受一个 *log.Logger:
package add
import "log"
func add(logger *log.Logger, x, y int) int {
logger.Printf("add %d %d", x, y)
res := x + y
logger.Printf("add %d %d = %d", x, y, res)
return res
}
接下来,我们更新测试。对于每次测试用例迭代,我们都会创建一个新的 bytes.Buffer 并为其绑定一个新的日志记录器。如果测试通过,缓冲区就会被直接丢弃。如果测试失败,我们会将缓冲区的内容与错误消息一起打印出来!
(注:我们还将测试升级为使用 t.Run,这是在 Go 中运行表格驱动子测试的地道方式)。
package add
import (
"bytes"
"log"
"testing"
)
func TestAdd(t *testing.T) {
inputs := []struct {
name string
x int
y int
want int
}{
{name: "1+2", x: 1, y: 2, want: 3},
{name: "3+2", x: 3, y: 2, want: 5},
{name: "4+5", x: 4, y: 5, want: 0}, // 故意失败的用例
}
for _, in := range inputs {
t.Run(in.name, func(t *testing.T) {
// 1. 创建一个缓冲区来捕获日志
var buf bytes.Buffer
// 2. 创建一个写入该缓冲区的新日志记录器
logger := log.New(&buf, "", log.LstdFlags)
// 3. 将自定义日志记录器传入我们的函数
got := add(logger, in.x, in.y)
if in.want != got {
// 4. 仅在测试失败时打印缓冲区内容
t.Errorf("got %d want %d\n\nExecution Logs:\n%s", got, in.want, buf.String())
}
})
}
}
现在,当我们运行测试时,输出非常干净且极具可操作性。我们只会看到与失败相关的日志:
$ go test
--- FAIL: TestAdd (0.00s)
--- FAIL: TestAdd/4+5 (0.00s)
add_test.go:35: got 9 want 0
Execution Logs:
2020/09/05 09:22:48 add 4 5
2020/09/05 09:22:48 add 4 5 = 9
FAIL
exit status 1
通过将函数与全局日志记录器解耦,我们让代码更加灵活、测试套件更加安静,并显著加快了调试过程。
获取《卡乌埃的生活方式》精选离线版——一份全面优化 🍏 健康、💰 财富、❤️ 爱 与 🧘 意义 的实践指南。
当我发布关于美好生活的新随笔、读书笔记和实践指南时接收通知。绝无垃圾邮件——可随时退订。
有任何问题、见解或建议吗?欢迎在下方分享您的想法。评论功能由 GitHub Discussions 驱动(无广告、无垃圾信息)——点击下方的使用 GitHub 登录即可发表评论或互动(注册免费 GitHub 账号不到一分钟即可完成)。
最后更新于 2026年10月11日.
免责声明:本站所有内容仅供信息参考与教育用途,不构成任何医疗、心理、财务、投资、税务或法律建议。此处呈现的观点、整理与见解仅代表我个人,不代表我雇主的立场。书籍、演讲及文章摘要均为独立的教育性评论,其知识产权归原作者所有。本站可能使用 AI 工具辅助编辑、排版与归纳;尽管我们竭力确保准确无误,但仍可能存在疏漏。隐私政策:本站使用第三方服务提供可选的邮件通讯订阅(follow.it)、评论(giscus / GitHub Discussions)以及嵌入媒体(YouTube),这些服务可能会依据各自的隐私政策处理访客的 IP 地址和 Cookie。