这个问题:如何在 Go 中测试 os.exit 场景(以及其中投票最高的答案)阐述了如何在 Go 中测试os.Exit()
场景。由于os.Exit()
不容易被拦截,使用的方法是重新调用二进制文件并检查退出值。此方法在Andrew Gerrand(Go 团队的核心成员之一)的演示文稿的幻灯片 23 中进行了描述;该代码非常简单,并在下面完整复制。
相关的测试和主文件看起来像这样(注意这对文件单独是一个 MVCE):
package foo
import (
"os"
"os/exec"
"testing"
)
func TestCrasher(t *testing.T) {
if os.Getenv("BE_CRASHER") == "1" {
Crasher() // This causes os.Exit(1) to be called
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestCrasher")
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
fmt.Printf("Error is %v\n", e)
return
}
t.Fatalf("process ran with err %v, want exit status 1", err)
}
和
package foo
import (
"fmt"
"os"
)
// Coverage testing thinks (incorrectly) that the func below is
// never being called
func Crasher() {
fmt.Println("Going down in flames!")
os.Exit(1)
}
但是,这种方法似乎受到某些限制:
使用 goveralls/coveralls.io 进行覆盖测试不起作用 - 例如,请参见此处的示例(与上面的代码相同,但为方便起见放入 github),它在此处生成覆盖测试,即它不记录正在运行的测试函数。请注意,您不需要这些链接来回答问题 - 上面的示例可以正常工作 - 它们只是为了展示如果您将上述内容放入 github 并通过 travis 一直到 coveralls.io 会发生什么
重新运行测试二进制文件似乎很脆弱。
具体来说,根据要求,这是覆盖失败的屏幕截图(而不是链接);红色阴影表示就 coveralls.io 而言,Crasher()
没有被调用。
有没有解决的办法?特别是第一点。
在 golang 级别,问题是这样的:
Goveralls 框架运行
go test -cover ...
,它调用上面的测试。上面的测试在
exec.Command / .Run
没有-cover
操作系统参数的情况下调用无条件地将
-cover
etc. 放入参数列表是没有吸引力的,因为它会在非覆盖测试中运行覆盖测试(作为子进程),并且解析参数列表是否存在-cover
etc. 似乎是一个繁重的解决方案。即使我将
-cover
等放在参数列表中,我的理解是我会将两个覆盖输出写入同一个文件,这是行不通的 - 这些需要以某种方式合并。我最接近的是这个 golang issue。
概括
我所追求的是一种运行覆盖测试的简单方法(最好通过 travis、goveralls 和 coveralls.io),其中可以同时测试被测试例程以 退出的OS.exit()
测试用例,以及记录该测试的覆盖率. 如果可以使其工作,我非常希望它使用上面的 re-exec 方法(如果可以工作的话)。
该解决方案应显示Crasher()
. 排除Crasher()
覆盖测试不是一种选择,因为在现实世界中,我想做的是测试一个更复杂的函数,在某些条件下,它会调用例如log.Fatalf()
;我要进行的覆盖测试是针对这些条件的测试可以正常工作。