`cmd/go: go test` 隐藏了测试二进制文件的退出状态,

ttisahbt  于 6个月前  发布在  Go
关注(0)|答案(5)|浏览(61)

你正在使用的Go版本是什么( go version )?

$ go version
go1.16.2 linux/amd6

这个问题在最新版本的发布中是否重现?

是的

你做了什么?

当一个测试二进制文件因某种错误而引发恐慌时,它会以代码2退出(就像所有其他Go二进制文件一样)。然而,go test隐藏了这个退出代码,总是以代码1退出。我能够从go test获得除0或1之外的退出代码的唯一方法是引发构建错误。引发恐慌的常见原因有:

  1. 测试本身由于某种错误而引发恐慌
  2. 达到-test.timeout,并引发恐慌。
    在这两种情况下,编译后的测试二进制文件的输出将只包含在恐慌之前运行的测试。没有输出未运行的测试。
    任何运行go test -json并希望尝试重新运行失败的测试(例如:gotestsum --rerun-fails)的程序都需要能够区分这两种情况:
  3. 所有测试都已运行,但某些测试失败 - 退出代码1
  4. 一些测试未运行,可能是因为恐慌 - (当前退出代码1)
    似乎目前无法检测到这两种情况,这使得安全地重新运行单个失败的测试变得不可能。如果运行测试的程序在发生恐慌后尝试重新运行测试,它只会知道已运行的测试,而不是任何未运行的测试。

你期望看到什么?

我希望go test始终使用编译后的测试二进制文件的退出代码。这样,如果测试二进制文件发生恐慌,go test将以代码2退出。如果出于某种原因不使用代码2用于此目的,任何非1的退出代码也都可以使用。

zpjtge22

zpjtge221#

从技术上讲,扫描所有测试输出并在任何行中查找 panic: 前缀是可能的,但这似乎既昂贵又容易出错。我希望使用退出代码是一个更简单、更安全的选择。

8qgya5xd

8qgya5xd2#

https://golang.org/cl/309250提到了这个问题:cmd/go: use exit code from compiled test binary on error

5lhxktic

5lhxktic3#

我想让go test始终使用编译后的测试二进制文件的退出代码。我认为这并不合理:通常情况下,go test可能调用多个二进制文件,那么如果它们返回不同的退出代码该怎么办?

tsm1rwdh

tsm1rwdh4#

看起来目前无法检测到[由于之前的恐慌而省略的测试],这使得无法安全地重新运行单个失败的测试。
这似乎是一个合理的担忧,但我认为我们应该更直接地解决它。也许我们可以在输出的末尾注入一个额外的 TestEvent ,以防发生恐慌?
或者,也许 #38382 中描述的行为实际上是这里的解决方案:目前看来,您可以通过 JSON 输出中存在具有 "Action":"run" 事件但没有相应事件(如 "Action":"pass""Action":"fail" )的 Test 来检测不完整的测试运行。如果测试用例开始但既未通过也未失败,则测试运行必然是不完整的,之后的测试可能丢失。
然而,这仍然留下了在运行测试之间、在所有测试完成后但在 TestMain 返回之前或在程序发生恐慌(或调用 os.Exit(1) )之前检测程序的问题。但也许可以通过运行 go test -list . 并将其与出现在 go test -json 输出中的测试进行比较来检测这种情况。

jhkqcmku

jhkqcmku5#

@bcmills 感谢你的快速回复。
我认为这并不一致:通常情况下,go test可能会调用多个二进制文件,那么如果它们返回不同的退出代码会发生什么呢?
在Go工具链(以及其他软件)中已经有了处理这种情况的明确模式:

  1. 如果一个二进制文件中的某个测试失败,而其他测试没有失败,会发生什么?go test 使用退出代码1(而不是0)
  2. 如果一个二进制文件无法编译,而另一个有失败的测试,会发生什么?go test 使用退出代码2(而不是0或1)
  3. SetExitStatus的实现已经编码了这种模式。任何“更严重”的问题(较高的退出代码)优先于较低的值。
    这对我来说似乎是合理的。用户请求执行所有这些测试,如果这个请求没有得到满足(所有测试都没有运行),用退出代码表示似乎很合适。即使某些包确实运行了所有测试也没关系。
    这似乎是一个合理的担忧,但我认为我们应该更直接地解决它。也许我们可以让go test在输出的末尾注入一个额外的TestEvent,以防发生恐慌?
    我考虑过这一点,但这样做似乎会更加复杂,可能存在更多的错误(因为有很多情况需要处理),并且可能实际上不会比简单的解决方案提供更多的价值。
    或者,也许在 #38382 中注意到的行为实际上是这里的解决方案:目前看来,你可以通过JSON输出中包含一个具有"Action":"run"但没有相应事件的Test来检测不完整的测试运行。
    #38382 描述了一个异常情况。大多数时候,当一个测试发生恐慌时,它确实会包含一个 "Action":"Fail" 事件。改变这一点将是一个破坏性的更改。例如:
{"Action":"output","Test":"TestPanics","Output":"--- FAIL: TestPanics (0.00s)\n"}
{"Action":"output","Test":"TestPanics","Output":"panic: this is a panic [recovered]\n"}
{"Action":"output","Test":"TestPanics","Output":"\tpanic: this is a panic\n"}
...
{"Action":"fail","Test":"TestPanics"}
{"Action":"output","Output":"FAIL\tgotest.tools/gotestsum/testjson/internal/frenzy\t0.003s\n"}
{"Action":"fail"}

这似乎是正确的行为。问题出在不完整的包上,而不是测试。
这仍然留下了在测试之间、在测试之后但在TestMain返回之前以及在所有测试完成后但在go test -json输出之前检测程序发生恐慌(或调用os.Exit(1))的问题。但也许可以通过运行 go test -list . 并将该列表与出现在 go test -json 输出中的测试进行比较来检测到这种情况。
这比简单地隐藏相关的退出代码要好得多吗?这似乎要复杂得多且容易出错。有很多事情可以改变测试列表、环境变量、构建标签,甚至是命令行参数。 go list 不太可能能够重现正确的测试列表。
另外,你能对当前行为是正确的原因进行评论吗?为什么 go test 应该隐藏实际的测试二进制文件的错误代码?

相关问题