Go Wiki:测试失败
如果您在 Go 项目的测试中发现失败,您应该怎么做?
测试目标
编写(和运行)Go 包的测试的目标是了解该包及其依赖项的行为。
Go 包的测试失败可能会向我们提供有关以下方面的信息
- 包或其依赖项中的实现缺陷,
- 对包 API 的错误假设,
- 测试本身的错误(例如对时机的无效假设),
- 意外的高资源需求(例如 RAM 或 CPU 时间),或
- 底层平台的错误或测试基础设施中的缺陷(可能需要升级或变通处理)。
在某些情况下,测试失败的原因可能不清楚:它可能是由以上多个条件引起的。就像重复科学实验一样,让测试多次失败有时可以从特定的失败模式中提供更多信息。
但是,如果测试失败没有告诉我们任何新信息,那么测试就没有达到其目的。
查找测试失败
测试失败通常会从以下地方注意到
- Go 构建仪表板,尤其是在构建者分类期间;
- TryBot 或 SlowBot 在待处理更改上的失败;
- 在特定包或包上运行
go test
,无论是工作在 Go 项目存储库内,还是作为用户自己的模块的一部分(例如go test all
); - 或在从源代码安装或测试贡献的更改时运行
all.bash
或all.bat
。
对测试失败进行分类
一旦我们注意到失败,我们需要对其进行分类。分类的目标是识别
- 失败的信息是否新?
- 谁最适合分析失败带来的新信息?
识别新信息
首先在打开的问题中搜索失败的关键细节,例如失败测试的名称和/或错误文本的其他独特片段(例如错误代码)。
如果您找到一个现有问题,首先检查问题讨论以查看失败是否已修复,以及您的失败信息是否贡献了相关的新的信息。如果是,请在问题评论中详细说明
- 描述您测试的 Go 版本(
go version
) - 您运行测试的方式和位置,例如
go env
输出- 您的机器和操作系统配置
- 您的网络配置和状态
- 您是否以及如何频繁地重现失败。
理想情况下,请附加或链接到完整的测试日志(可能在<details>
块中)。
如果您没有找到现有问题,请创建一个新的问题。
提交问题
粘贴足够的测试日志,以便未来的报告者能够搜索问题,包括测试的名称及其日志输出的任何独特部分。(如果测试日志很长——例如,如果它包含大型协程转储——请考虑发布较短的摘录和/或将完整的失败消息放在<details>
块中。)
您可以使用fetchlogs
和greplogs
工具在构建仪表板中搜索类似的失败
# download recent logs
fetchlogs -n 1024 -repo all
# search logs for some regexp describing the failure message
greplogs -l -e $FAILURE_REGEXP
如果失败似乎特定于某个包,请咨询https://dev.golang.org/owners以找到该包的维护者并在问题中提及他们。(如果包中没有列出所有者,请在https://cs.opensource.google/go中检查包的最近历史记录和/或升级到可以帮助识别相关所有者的某个人——并考虑使用您学到的知识更新所有者表!)
如果失败似乎特定于GOOS
或GOARCH
,请在问题中添加相应的 GOOS 和/或 GOARCH 标签,并在问题中提及@golang/port-maintainers的相关子团队。
如果失败似乎影响至少一个一等端口,请将问题添加到当前的发行里程碑中,并将其标记为release-blocker
。否则,将问题添加到Backlog
里程碑中。
如果失败似乎特定于构建者(例如网络连接问题,或需要系统更新的平台错误),请咨询x/build/dashboard/builders.go
以找到该构建者的维护者并在问题中提及他们。(对于没有明确列出维护者的构建者,请改为提及@golang/release团队。)
解决测试失败
一旦为测试失败提交了问题,相关的包、端口和/或构建者维护者应检查从测试失败中获得的信息,并决定如何通过执行以下一项或多项操作来解决它
- 回滚对代码或测试基础设施的更改,这些更改被认为引入了问题,
- 修复(或对)失败的根本原因进行变通处理,
- 收集更多信息,通过订阅问题更新和/或运行更多测试,
- 报告依赖项、平台或测试基础设施中的底层缺陷,和/或
- 降低优先级失败,通过在受影响的平台上跳过失败(或将这些平台标记为已损坏),然后将问题移至未来的里程碑或
Backlog
里程碑和/或删除release-blocker
标签。
当维护者决定降低测试失败的优先级时,他们确定测试的额外失败将不会提供有用的新信息。在那时,测试不再履行其目的,维护者应该抑制失败——通常通过添加对testenv.SkipFlaky
或t.Skipf
的调用。
跳过测试失败
当我们添加对testenv.SkipFlaky
的调用时,我们的目标是消除不提供新信息的失败模式,同时尽可能保留测试的价值。
-
如果观察到的失败只是测试的几种可能的失败模式之一,则仅针对该失败模式跳过测试。
- 例如,如果错误始终是特定内容(例如
syscall.ECONNRESET
),请使用errors.Is
检查该特定错误。
- 例如,如果错误始终是特定内容(例如
-
如果认为失败会影响特定
GOOS
和/或GOARCH
的所有版本,或者无法识别受影响的版本,请针对runtime.GOOS
和/或runtime.GOARCH
进行检查,并仅跳过受影响的平台。 -
如果失败是由于平台的特定版本上的错误造成的,请根据
testenv.Builder
或GO_BUILDER_NAME
环境变量跳过测试。(如果测试对外部 Go 用户失败,他们可以选择升级到平台的不受影响版本——他们可能应该看到测试失败以发现错误的存在!)- 还要考虑添加另一个环境变量,用户和贡献者可以设置该环境变量以确认错误并抑制失败。
将构建者或端口标记为已损坏
平台错误或核心包(例如os
、net
或runtime
)中的错误可能会影响如此多的测试,以至于失败不可行跳过,或者可能会在编译或链接时表现为失败。如果发生此类错误,选择范围更有限:如果我们无法回滚更改或修复或变通处理根本原因,并且不需要收集更多信息,我们只能通过将整个构建者或平台标记为已损坏来降低失败的优先级。
要将构建者标记为已损坏,请编辑其在x/build/dashboard/builders.go
中的配置,以在KnownIssue
字段中添加一个问题;请注意,在仪表板分类期间,通常会跳过存在已知问题的构建者。
针对一等端口的已损坏构建者应将其已知问题标记为release-blocker
,直至决定修复构建者或停止支持受影响的平台版本。
如果次要端口的所有构建者都已损坏,则该端口本身可能被视为已损坏。讨论 #53060旨在解决如何处理损坏的次要端口的问题。
此内容是Go Wiki的一部分。