Terraform AWS Provider 验收测试与单元测试审查指南:从review-tests技能看测试规范
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
导读
本篇文章以开源仓库terraform-provider-aws中面向代码审查者的.agents/skills/review-tests/SKILL.md为核心,系统讲解该仓库为「每个资源」制定的一套验收测试(Acceptance Test)与单元测试(Unit Test)基础规范,包括必须存在的_basic/_disappears测试、TestAcc命名规则、TestCase必备字段(PreCheck、ErrorCheck、ProtoV5ProviderFactories、CheckDestroy)、随机命名、ImportState步骤与正则/ARN 断言等。读完本文,你将能独立审查internal/service/**/*_test.go变更,准确识别测试缺失、命名错误与反模式,并能结合源码理解每条规范的底层实现。
适用范围与定位
review-tests是仓库.agents目录下的一组「审查技能」(skills)之一,与 breaking-changes、review-schema、review-tags、review-identity 等并列,通常由review-pr加载,用于审查修改了internal/service/**/*_test.go的 Pull Request。
需要特别说明的是本技能的范围边界:
- 范围内:按资源(per-resource)维度的验收测试基础要素;
- 范围外:Ephemeral Resource、Provider Functions、Actions 类测试不在此技能覆盖范围内;
- 配套技能:Exists/Destroy 检查、数据源(data source)测试、列表资源(list resource)测试以及单元测试的深入内容,由
review-tests-helpers技能补充。
在开始逐条规范前,先理解一个整体原则:review-tests假定审查者扮演@maintainer角色,即以维护者视角审视测试代码是否符合仓库长期沉淀的约定。
新资源必须配备的两个测试
SKILL 明确规定:为仓库新增资源时,以下两个测试是硬性要求。
TestAcc<Service><Resource>_basic
这是「完整快乐路径」测试,覆盖资源从创建、读取到属性校验的全过程,并且必须以一个ImportState步骤收尾(Import 步骤规范详见后文)。_basic测试中应当尽可能对全部属性做断言,而不是只检查 ID。
以 EC2 服务的 vpc_test.go 为例,TestAccVPC_basic的Check块使用resource.ComposeAggregateTestCheckFunc一次性断言了 ARN、CIDR、DNS 相关布尔值、实例租期、ipv6_association_id空值、owner ID、tags 等十余个属性,最后以ImportState步骤结尾,是_basic测试的典型范式。
TestAcc<Service><Resource>_disappears
该测试用于验证「资源在 Terraform 之外被删除后,Provider 能否将其重建」。测试思路是:先正常创建资源,然后在Check中调用仓库提供的 disappears 辅助函数带外(out-of-band)删除远端资源,再通过断言产生非空计划(ExpectNonEmptyPlan: true)以及计划动作检查,验证下一次 apply 会重新创建资源。
同样在 vpc_test.go 中可以看到完整的TestAccVPC_disappears:它调用acctest.CheckSDKResourceDisappears删除 VPC,并借助plancheck.ExpectResourceAction断言资源动作应为Create(重建)。值得注意的是,SKILL 还要求:凡是为资源声明了@Tags与 identity 注解的,标签测试(_tags*)与身份测试(_Identity_*)应由生成器自动生成,因此审查 PR 时若发现新资源出现了手写的这类测试,应当标记出来。
测试命名规范
命名是测试可读性与可维护性的第一道关卡。SKILL 给出的命名规则如下:
| 测试类型 | 命名模板 | 示例 |
|---|---|---|
| 资源验收测试 | TestAcc<Service><Resource>_<scenario> | TestAccVPC_basic、TestAccVPC_disappears |
| 数据源验收测试 | TestAcc<Service><DataSource>DataSource_<scenario> | TestAccVPCDataSource_ipv6 |
| 列表资源验收测试 | TestAcc<Service><Resource>_List_<scenario> | TestAccVPCPeeringConnection_List_... |
| 单元测试 | 任意不带TestAcc前缀的名字 | TestFlattenXxx |
审查要点:
- 单元测试严禁调用 AWS。凡是不以
TestAcc开头的测试,如果内部发起了 AWS API 调用,审查时必须标记为错误; - 服务名使用仓库
names包统一管理的大小写风格(如VPC、S3、RDS),保证跨服务一致。
TestCase 必备四要素
SKILL 给出所有验收测试的统一开头模板:
ctx := acctest.Context(t) acctest.ParallelTest(ctx, t, resource.TestCase{ // ... })TestCase内必须设置以下四个字段,缺一不可;审查时发现缺失或被替换版本都要标记。
1.PreCheck:三层前置检查
PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.<Service>EndpointID) <pkg>_testAccPreCheck(ctx, t) }acctest.PreCheck:位于 internal/acctest/acctest.go,负责校验测试凭证(要求AWS_PROFILE、AWS_ACCESS_KEY_ID或容器凭证三者至少其一,且静态密钥必须配AWS_SECRET_ACCESS_KEY),并初始化全局测试 Provider(Provider.Configure),同时把区域默认值写入环境变量,使测试配置可以省略 provider 块。acctest.PreCheckPartitionHasService:位于 internal/acctest/acctest.go,用 AWS 分区端点表检查当前分区是否提供该服务,不提供则直接t.Skipf跳过。testAccPreCheck:服务包自己实现的检查,具体规范见下文「PreCheck 模式」小节。
2.ErrorCheck:服务级错误过滤
ErrorCheck: acctest.ErrorCheck(t, names.<Service>ServiceID)ErrorCheck 的实现会按服务 ID 查找该服务注册的错误检查函数,并统一过滤「该分区不支持此特性 / 未知操作」之类的公共错误(errorCheckCommon匹配is not supported in this、InvalidAction、UnknownOperationException、UnsupportedOperation等字符串),命中后自动t.Skipf,让测试在特性未开通的账户/分区上优雅跳过而不是失败。
3.ProtoV5ProviderFactories:Provider 工厂
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories该字段通过仓库封装的多区域/多账户工厂族(acctest.go 中的ProtoV5FactoriesNamed、ProtoV5FactoriesAlternate、ProtoV5FactoriesAlternateAccountAndAlternateRegion、ProtoV5FactoriesMultipleRegions等)为测试提供基于 Protocol v5 的 provider 服务器实例。对于跨区域、跨账户等复杂场景,应使用对应的多 Provider 变体而非默认工厂。
4.CheckDestroy:销毁校验
CheckDestroy: testAccCheck<Resource>Destroy(ctx, t)每个资源测试都要注册自己的 Destroy 检查函数,用于确认测试结束时远端资源确实被清理,防止测试产生遗留资源。
长耗时测试的守卫
对于预计运行超过约 5 分钟的测试,SKILL 要求在acctest.Context(t)之后立刻加入短模式守卫:
if testing.Short() { t.Skip("skipping long-running test in short mode") }这样 CI 跑go test -short时能快速跳过耗时用例。
随机命名规范
测试中创建的资源名称必须随机,避免硬编码冲突。规范要求统一使用:
sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)其中acctest.ResourcePrefix在 internal/acctest/acctest.go 中定义为"tf-acc-test",因此生成的名称形如tf-acc-test-<随机数>。审查时应标记以下两种情况:
- 硬编码的名称;
- 裸用
acctest.RandString(...)(不带前缀)。
顺带说明,仓库还在 vcr.go 中提供了 VCR 友好的acctest.RandomWithPrefix变体:当 VCR(测试录制回放)启用时使用确定性随机源,保证回放可复现。
PreCheck 模式:单次调用 + 跳过语义
testAccPreCheck的实现有严格的纪律要求。SKILL 给出的准则是:
- 只发起一次廉价的 List/Describe 调用;
- 命中分区/权限类错误时通过
acctest.PreCheckSkipError(err)判断并t.Skipf跳过; - 其他真实错误才
t.Fatalf。
仓库实际样例见 internal/service/ec2/transitgateway_default_route_table_association_test.go:
func testAccPreCheck(ctx context.Context, t *testing.T) { conn := acctest.ProviderMeta(ctx, t).EC2Client(ctx) input := &ec2.DescribeTransitGatewaysInput{} _, err := conn.DescribeTransitGateways(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) } if err != nil { t.Fatalf("unexpected PreCheck error: %s", err) } }PreCheckSkipError实现在 internal/acctest/acctest.go,它把「分区未开通该 API」类错误统一判为可跳过:包括AccessDeniedException(GovCloud 中端点存在但未启用)、UnknownOperationException、UnsupportedOperation、InvalidAction、ForbiddenException、DNS 解析失败等。审查时要标记以下反模式:
- PreCheck 发起多次 API 调用;
- 直接
return err而不是t.Skipf/t.Fatalf; - 遗漏
PreCheckSkipError判断(导致未开通特性在 CI 上误报失败)。
ImportState 步骤
_basic测试的最后一个步骤必须是导入校验:
{ ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }其中ImportStateVerify: true表示导入后要与现有状态做一致性比对。对于 AWS API 不返回的只写字段(如密码、apply_immediately这类需要立即生效的参数),应使用ImportStateVerifyIgnore显式声明忽略:
ImportStateVerifyIgnore: []string{"password", "apply_immediately"},审查要点:如果发现某个测试使用大而全的忽略列表来掩盖真实的漂移(drift)问题,必须标记——忽略列表只能用于真正的只写字段,不能用来掩盖状态不一致的缺陷。
Disappears 测试的两种写法
_disappears测试中「带外删除资源」的辅助函数按资源实现框架分为两种:
- Terraform Plugin Framework 资源:
acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tf<svc>.Resource<Name>, resourceName)- Terraform Plugin SDKv2 资源:
acctest.CheckSDKResourceDisappears(ctx, acctest.Provider, tf<svc>.Resource<Name>(), resourceName)(注意 SDKv2 变体传入的是资源构造函数的调用结果,带(),返回*schema.Resource。)
从实现上看,framework.go 中的CheckFrameworkResourceDisappears会为资源构造一个仅含顶层字符串属性的简化 Framework State,然后直接调用该资源的Delete方法完成带外删除;对于依赖嵌套/非字符串参数才能执行删除的资源,SKILL 配套提供了CheckFrameworkResourceDisappearsWithStateFunc,允许通过自定义 state 函数补全删除所需状态。仓库还针对按区域覆盖的资源做了容错处理:若删除时出现 "Value Conversion Error",会向 schema 注入顶层region属性后重试(见 framework.go)。
正则与 ARN 断言规范
必须使用regexache
SKILL 明确要求测试中的正则表达式使用github.com/YakDriver/regexache包,而不是 Go 标准库的regexp。审查时若发现新测试import "regexp",直接标记。仓库中的实际用例(如vpc_test.go中regexache.MustCompile(\vpc/vpc-.+`)`)也印证了这一约定。
ARN 断言:使用区域感知的匹配函数
对于 ARN 类属性,规范要求使用仓库提供的区域感知匹配函数,而不是手工拼接:
| 场景 | 推荐函数 |
|---|---|
| 当前区域 ARN | acctest.MatchResourceAttrRegionalARN(ctx, resourceName, attr, service, regexp) |
| 指定区域 ARN | acctest.MatchResourceAttrRegionalARNRegion(...) |
| 无账号段 ARN | acctest.MatchResourceAttrRegionalARNNoAccount(...) |
| 指定账号 ARN | acctest.MatchResourceAttrRegionalARNAccountID(...) |
这些函数集中定义在 internal/acctest/acctest.go 附近。它们会根据测试当前所在分区与区域自动推导 ARN 前缀与账号段,从而消除测试对硬编码账号/区域的依赖。审查时应标记**用fmt.Sprintf手工拼 ARN(包含账号 ID 或区域)**的做法。
vpc_test.go中的一行是标准用法:
acctest.MatchResourceAttrRegionalARN(ctx, resourceName, names.AttrARN, "ec2", regexache.MustCompile(`vpc/vpc-.+`))审查清单速查
结合上文,把 SKILL 的要点浓缩为一份可执行的 PR 审查清单:
- 必备测试:新资源是否同时具备
_basic(含 ImportState 收尾、全属性断言)与_disappears?带@Tags/ identity 注解的资源是否删除了手写的_tags*/_Identity_*测试(应由生成器生成)? - 命名:验收测试是否遵循
TestAcc<Service><Resource>_<scenario>?数据源/列表资源是否用了各自的DataSource_/_List_后缀?单元测试是否误加TestAcc前缀、或出现调用 AWS 的单元测试? - TestCase 四要素:
PreCheck(三层)、ErrorCheck: acctest.ErrorCheck(t, names.<Service>ServiceID)、ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories、CheckDestroy是否齐全且未被替换? - 长测试守卫:预计超过约 5 分钟的测试是否在
acctest.Context(t)后添加testing.Short()跳过逻辑? - 随机命名:是否使用
sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)?有没有硬编码名称或裸acctest.RandString? - PreCheck 模式:
testAccPreCheck是否只做一次廉价 API 调用、是否通过PreCheckSkipError支持跳过? - Import:忽略列表是否只覆盖真正的只写字段?
- 正则与 ARN:是否用
regexache替代regexp?ARN 断言是否使用区域感知函数而非fmt.Sprintf手工拼接?
延伸阅读
- 配套技能:review-tests-helpers(Exists/Destroy 检查与数据源、列表资源、单元测试的深入规范)
- 同族审查技能:review-schema、review-tags、review-identity、review-lifecycle
- 测试辅助实现:internal/acctest/acctest.go、internal/acctest/context.go、internal/acctest/framework.go、internal/acctest/vcr.go
- 权威测试范例:internal/service/ec2/vpc_test.go
- 运行验收测试的环境变量与操作说明:docs/running-and-writing-acceptance-tests.md
- 单元测试编写指南:docs/unit-tests.md
【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考