首页
/ Moby 测试体系完全指南:测试套件划分、编写规范与 make 测试命令实战

Moby 测试体系完全指南:测试套件划分、编写规范与 make 测试命令实战

2026-09-03 16:11:11作者:余洋婵Anita

Moby(Docker 引擎的开源仓库)为贡献者提供了一套清晰分层的测试体系:快速的单元测试(unit tests)、面向 HTTP API 的集成测试(integration/),以及已经弃用的遗留套件(integration-cli/)。本文基于仓库根目录的 TESTING.md 展开,结合 Makefilehack/test/unithack/make/test-integration 等脚本源码,系统讲解 Moby 的测试分层原则、新测试编写规范、集成测试环境判断技巧,以及如何用 make 目标和环境变量精确控制测试的运行范围,帮助你在修改 Moby 代码后能够正确选择并运行对应的测试套件。

一、Moby 的测试套件分层

Moby 将测试划分为两套正式套件加一套遗留套件:

套件 位置 工具与断言 定位
单元测试(Unit tests) 与源码同包 go test + gotest.tools/v3/assert 要求快速,只测试自身所在包
API 集成测试 ./integration/<component>(container、image、volume 等) go test + gotest.tools/v3/assert 向 API 端点发起真实 HTTP 请求,校验 HTTP 响应与调用后的 daemon 状态
遗留集成测试(已弃用) integration-cli/ 旧框架 不再接受新测试,需要更新的旧测试应迁移到单元测试或新 API 集成测试套件

从仓库结构看,integration/ 下按组件划分子目录,例如 integration/container/integration/image/integration/volume/ 等,每个目录下是对应组件的端到端测试;而 integration/internal/ 存放跨组件共享的测试辅助代码(容器/镜像构建工具、环境要求判断、终端输出校验等)。integration-cli/ 虽仍存在大量 docker_*_test.go 文件,但按 TESTING.md 的明确规定,它已被标记为 deprecated,新提交到该目录的测试会被 CI 直接拒绝。

二、编写新测试的规范

2.1 新功能(New Features)的测试要求

TESTING.md 对新功能提出的核心原则是:新增代码首先应由单元测试覆盖。如果一段代码难以用单元测试测试,这恰恰是它需要重构的信号——文档建议将依赖从具体结构体改为非导出接口(unexported interfaces),以便在测试中注入 fake 实现。

集成测试的边界同样被严格限定:

  • 若新功能引入了全新的 API 端点,则必须新增一个 API 集成测试,覆盖该端点的成功路径(success case);
  • 若只是为已有端点新增 API 字段,应把新字段加到该端点的现有集成测试中,而不是新建测试;
  • 不要为每一个新 API 字段或 API 错误分支单独添加集成测试——错误分支(error cases)应由单元测试负责。

2.2 Bug 修复(Bug Fixes)的测试要求

  • 修复必须附带一个实际触发该 bug 路径的单元测试用例
  • 如果 bug 涉及某个 API 端点的行为,可以在该端点的现有集成测试中追加新的断言(new assertions),而非新写一个集成测试。

2.3 新集成测试必须放在 integration/

再次强调:integration-cli 中的新测试会被 CI 拒绝,所有新集成测试都应实现在 integration/ 目录下。

2.4 集成测试的环境条件判断:skip.If

集成测试运行环境千差万别(本地 daemon、远程 daemon、rootless 模式、Windows 等),因此新增或修改 integration/ 下的测试时必须考虑环境适配。TESTING.md 给出的做法是使用 gotest.tools/v3/skip 包的 skip.If 让测试按条件运行。官方示例:

package example

import (
	"testing"

	"gotest.tools/v3/skip"
)

func TestSomething(t *testing.T) {
	skip.If(t, testEnv.IsRemoteDaemon(), "test requires a local daemon")

	// your integration test code
}

当检测到远程 daemon 时测试会被跳过。所有可用的环境条件定义在 internal/testutil/environment/environment.go 中。从该源码可以看到 Execution 类型提供了若干条件判断方法,例如:

  • IsRemoteDaemon()——被测 daemon 是否位于另一台主机(源码注释:“true if the daemon under test is on different host”);
  • IsRootless()——是否启用了 rootless 模式;
  • RuntimeIsWindowsContainerd()——Windows 上是否使用 containerd 运行时。

仓库内的真实用法可参考 integration/build/build_cgroupns_linux_test.go

skip.If(t, testEnv.DaemonInfo.OSType != "linux")
skip.If(t, testEnv.IsRemoteDaemon())
skip.If(t, !requirement.CgroupNamespacesEnabled())

这展示了典型的三层过滤:操作系统类型 → daemon 位置 → 特性能力(requirement 包来自 integration/internal/requirement/)。

三、运行测试:make 目标与环境变量

3.1 单元测试:make test-unit

基本用法:

make test-unit

或者在 make shell 启动的容器 / 配置好的环境中直接运行 hack/test/unit

两个可控制测试子集的环境变量:

  • TESTDIRS——要测试的目录列表,默认 ./...
  • TESTFLAGS——传给 go test 的额外参数;按名称模式过滤时写 TESTFLAGS="-test.run TestNameOrPrefix"

Makefile 中的对应目标确认了这条调用链:

.PHONY: test-unit
test-unit: build ## run the unit tests
	$(DOCKER_RUN_DOCKER) hack/test/unit

test-unit 先构建 docker-dev 镜像,再在容器内执行 hack/test/unit

3.2 集成测试:make test-integration

基本用法:

make test-integration

该 make 目标会同时运行 “integration” 与 “integration-cli” 两套套件。相关控制变量:

环境变量 作用
TEST_INTEGRATION_DIR 指定要构建和运行哪些集成测试目录
TEST_SKIP_INTEGRATION 设置任意值即跳过 integration 套件
TEST_SKIP_INTEGRATION_CLI 设置任意值即跳过 integration-cli 套件
TESTFLAGS_INTEGRATION / TESTFLAGS_INTEGRATION_CLI 分别为两套套件传递专属 flag
TEST_FILTER 测试过滤器,直接透传给 go test -run(integration-cli 对应 go test -check-f),并自动联动设置上述其它变量

Makefile 展示了跳过逻辑的实现:当 TEST_SKIP_INTEGRATIONTEST_SKIP_INTEGRATION_CLI 同时非空时,目标直接打印 “Both integrations suites skipped per environment variables” 并退出;否则进入容器执行 hack/make.sh dynbinary test-integration。而 hack/make/test-integration 脚本则负责实际的测试编排:构建测试二进制(build_test_suite_binaries)→ 启动被测 daemon(bundle .integration-daemon-start)→ 循环执行 run_test_integration → 无论成败都执行 .integration-daemon-stop 与二进制清理,并将全过程输出追加到 test.log

此外 Makefile 还提供 test-integration-flaky 目标,用于对新集成测试做压力重跑,排查不稳定(flaky)用例;Makefile 的总目标 make test 则会串联 build、test-unittest-integrationtest-docker-py

3.3 指定 Go 版本

通过 GO_VERSION 变量可以切换用于构建被测试代码的 Go 版本,例如:

make GO_VERSION=1.24.8 test

四、源码纵深:hack/test/unit 的测试编排细节

hack/test/unit 脚本揭示了 make test-unit 背后的关键工程细节,这些细节直接影响你使用 TESTDIRS / TESTFLAGS 时的行为:

  1. api 与 client 是独立 Go 模块,单独执行。脚本开头(hack/test/unit#L26-L54)会判断 TESTDIRS 是否命中 ./api./client,命中后分别进入对应模块目录,用 gotestsum ... -mod=readonly 单独运行。原因是仓库存在 vendor/ 目录,若不排除 vendor 模式,api/client 模块的测试文件可能不被识别;注释中特别引用了 Go 官方文档对 -mod=readonly 的解释——忽略 vendor 目录,并在 go.mod 需要更新时报错。
  2. 默认排除 vendor 与 integration。主模块的包列表来自 go list "$TESTDIRS" | grep -vE '(/vendor/|/integration)'hack/test/unit#L91-L95),确保 make test-unit 不会误跑集成测试。
  3. libnetwork 强制串行执行。由于 libnetwork 测试会调用 iptables,脚本将其拆出并用 -p=1 顺序执行(hack/test/unit#L124-L135)。
  4. flaky 测试单独重跑。第一轮会用 -skip=TestFlaky.* 跳过 flaky 用例,结束后再用 --rerun-fails=4TestFlaky.* 单独重跑(hack/test/unit#L137-L148);如果你在 TESTFLAGS 中显式传了 -run,则不会进行这轮单独的 flaky 重跑。
  5. 统一使用 gotestsum 产出报告。各模块分别输出 JSON/JUnit 报告到 bundles/ 目录(如 bundles/junit-report.xmlbundles/coverage.out),并固定 -test.timeout=${TIMEOUT:-5m} 超时(hack/test/unit#L16)。
  6. 构建标签固定为 netgo journaldBUILDFLAGS),这与官方构建保持一致。

对于需要 docker-proxylibnetwork/drivers/bridge 包,脚本还会自动调用 hack/make.sh binary-proxy install-proxy 补装依赖(hack/test/unit#L97-L100)。

五、实践清单:按场景选择测试命令

结合上述机制,日常开发中的典型用法如下(均相对仓库根目录):

# 全量单元测试
make test-unit

# 只测某包(等价于 hack/test/unit 的 TESTDIRS)
make TESTDIRS='./daemon/...' test-unit

# 按名称模式过滤
make TESTFLAGS="-test.run TestContainerCreate" test-unit

# 只跑新集成测试套件,跳过遗留套件
make TEST_SKIP_INTEGRATION_CLI=1 test-integration

# 只跑 integration 下指定组件目录
make TEST_INTEGRATION_DIR="container image" test-integration

# 用过滤器直接筛测试名
make TEST_FILTER="TestLogs" test-integration

# 换 Go 版本跑完整测试
make GO_VERSION=1.24.8 test

适用前提与限制需要留意:这些 make 目标都依赖 build(构建 docker-dev 镜像)并在容器内执行,因此宿主机需要先具备可用的 Docker;TEST_FILTER 对 integration-cli 透传的是 -check-f 而非 -run,两套套件的行为差异在 TESTING.md 中已明确说明。

六、小结

Moby 的测试体系可以归纳为三条主线:

  • 分层清晰:单元测试追求快速且只测本包;集成测试面向真实 daemon 走 HTTP 协议;遗留套件 integration-cli/ 冻结不再扩张(TESTING.md)。
  • 编写有章法:新功能必须有单测覆盖、仅全新 API 端点才新增集成测试、错误分支归单元测试;集成测试用 skip.If + internal/testutil/environment/environment.go 适配环境(TESTING.md)。
  • 运行可控make test-unit / make test-integration 配合 TESTDIRSTESTFLAGSTEST_INTEGRATION_DIRTEST_SKIP_INTEGRATION*TEST_FILTERGO_VERSION 等变量精确控制范围(Makefile),底层由 hack/test/unithack/make/test-integration 完成多模块、多套件的自动化编排。
登录后查看全文
热门项目推荐
相关项目推荐