envtest本地集成测试必须显式启动api server和etcd,调用start()获取config并注入client,用defer stop()清理;默认不加载crd需手动安装;推荐用fake.client而非mock,注意conversion webhook、status.conditions更新和时间断言等细节。

envtest 本地集成测试必须显式启动 API Server
envtest 不是“开箱即用”的模拟环境,它依赖本地临时启动的 etcd + kube-apiserver 进程。不调用 envtest.Environment.Start() 就直接跑 client.List(),会 panic 报错 connection refused 或 no route to host。
实操建议:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 在
TestMain或每个测试函数开头初始化并启动 envtest:先 newenvtest.Environment,再调用Start(),拿到Config后注入到 test client - 务必用
defer env.Stop()清理,否则多次go test可能因端口占用失败 - envtest 默认不加载 CRD,需手动调用
cfg, err := env.Start()后,用ctrl.NewManager加载 scheme 并调用crd.Install()(来自api/v1/zz_generated.deepcopy.go) - 避免在测试中复用全局
envtest.Environment实例——并发测试时可能冲突;每个测试应独占一套环境或加sync.Once控制
Reconcile 单元测试别 mock client.Client,改用 fake.Client
直接用 gomock 或手写 mock 实现 client.Client 接口,容易漏掉 Get() 和 List() 的 cache 行为差异,导致测试通过但线上行为异常。fake.Client 是 controller-runtime 官方提供的内存客户端,能正确模拟 informer cache 的读取路径和 resourceVersion 语义。
实操建议:
- 用
fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...)构建 client,objs包含你的 CR 实例、关联的 Deployment、Service 等 - 若测试需要触发 “资源不存在 → 创建” 流程,
WithObjects别传入目标子资源;若测 “存在 → 更新”,则必须传入带正确ResourceVersion的对象 - 不要对 fake.Client 做
client.Status().Update()—— 它不支持 subresource,status 更新需走普通client.Update()并确保 struct tag 有subresource:"status" - 验证写操作是否发生,用
fakeClient.Get()二次读取比对字段,而不是检查 mock 调用次数
CRD 升级测试必须覆盖 conversion webhook 路径
CRD 从 v1alpha1 升到 v1 后,旧版本对象在 kubectl get 时如果报 error unmarshaling JSON,说明 conversion webhook 没生效或没配对。仅测新对象创建无法暴露这个问题。
实操建议:
- 测试前先用
kubectl apply -f crd-v1alpha1.yaml部署老版本 CRD,并创建一个实例 - 再部署新 CRD(含
conversion.webhook配置),确保config/crd/kustomization.yaml中patchesStrategicMerge已启用且指向 conversion patch 文件 - 用
curl -k -X POST https://localhost:9443/convert手动发 conversion 请求(body 含老格式对象),检查返回是否为新格式且无字段丢失 - 在 envtest 中启动 webhook server(如
env.WebhookInstallOptions),并注册 conversion handler;否则fake.Client无法触发 conversion 逻辑
测试 status.conditions 更新要检查 lastTransitionTime 和 reason
只断言 status.conditions[0].type == "Ready" 和 status == "True" 不够——Kubernetes 的 condition 渲染和告警逻辑严重依赖 lastTransitionTime 是否更新、reason 是否准确。status 字段没变但 time 不更新,kubectl get 会显示陈旧状态。
实操建议:
- 每次调用
updateStatusCondition()后,立刻用fakeClient.Get()读回对象,检查cond.LastTransitionTime.Time.After(before) -
reason字段必须匹配你业务中的关键错误码,比如"ConfigApplyFailed"而不是泛泛的"Failed",否则 Prometheus rule 或 kubectl 插件无法做聚合 - 避免在测试里用
time.Now()做断言——用metav1.Now()或记录起始时间点后比较 - 多个 condition 共存时(如
Available、Progressing),确保它们互斥或满足业务定义的合法组合,例如Progressing==True时Available必须为Unknown
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










