Go test 如何运行项目的所有测试:深入剖析内部机制

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

如何在 Go 项目中运行所有测试以及“内部”发生了什么?

\n

每个 Go 开发者都曾在终端中输入过神奇的 go test ./... 命令,并看着绿色的勾号在屏幕上滚动。但您是否想过,这一刻到底发生了什么?为什么测试会以这种方式运行,而不是其他方式?为什么有时 go test 会默默地跳过您的新测试?

\n

在本文中,我们不仅会回答 Stack Overflow 上的热门问题(该问题已获得数千次浏览),还会深入 Go 编译器的内部,了解测试机制是如何构建的。开始吧!

\n\n

为什么 go test ./... 如此神奇?

\n

让我们从基础开始。不带参数的 go test 命令只运行当前包中的测试。但如果您的项目有几十个包(在实际生产环境中通常如此),手动逐个遍历它们无异于中世纪酷刑。这时 ./... 就派上用场了。

\n

这个模式的意思是:“从当前目录开始,递归查找所有包”。Go 会遍历目录树,忽略 vendor 和模块,并收集所有包的列表。对于每个包,它会启动一个独立的 go test 实例。

\n

但是,如果您只想在特定目录中运行测试呢?例如,只在 ./internal/... 中?同样有效:go test ./internal/...。

\n

重要提示:如果您使用 Go Modules(从 Go 1.16+ 开始是标准),那么 ./... 仅在当前模块内有效。要测试工作区中的所有模块,您必须在每个模块中使用 go test ./...,或者配置 go.work。

\n\n

在 go test 的上下文中,“包”是什么?

\n

Go 使用 包 的概念——同一目录中的一组 .go 文件。测试文件(带有 _test.go 后缀)也被视为包的一部分,但会单独编译。当您运行 go test 时,Go 会编译一个测试二进制文件,其中包含:

\n
    \n
  • 包的源代码(但仅限测试中使用的部分)
  • \n
  • 测试本身的代码
  • \n
  • 用于运行和报告的特殊运行时包装
  • \n
\n

这个二进制文件会被执行,运行测试并输出结果。整个过程对每个包都是独立的。

\n\n

Go 如何决定运行哪些测试?

\n

当您输入 go test ./... 时,Go 会执行以下步骤:

\n
    \n
  1. 遍历文件系统——Go 找到所有包含 .go 文件的目录。
  2. \n
  3. 过滤——忽略 vendor、testdata 目录和隐藏文件夹(以 . 开头的)。
  4. \n
  5. 编译——为每个包创建一个临时二进制文件。
  6. \n
  7. 运行——执行二进制文件,收集结果。
  8. \n
  9. 缓存——如果代码没有变化,Go 会使用缓存并显示 (cached)。
  10. \n
\n

最后一点是常见的惊讶原因。您修改了测试,运行 go test ./...,但它显示“ok (cached)”。为什么?因为 Go 根据输入文件的哈希值缓存结果。如果您只修改了注释或未使用的导入,哈希值不会改变,测试也不会重新运行。

\n

小技巧:可以使用 go clean -testcache 命令强制清除缓存。或者使用 -count=1 标志,它强制 Go 忽略缓存:go test -count=1 ./...。

\n\n

并行性:为什么测试并不总是更快?

\n

默认情况下,go test 在单个包内顺序运行测试。但不同包之间是并行运行的!同时运行的包数量由 -p 标志决定(默认等于 CPU 数量)。

\n

听起来很棒,但有一个问题:

博客

书籍推荐