There's a presentation by Andrew Gerrand (one of the core members of the Go team) where he shows how to do it.
Given a function (in main.go
)
package main
import (
"fmt"
"os"
)
func Crasher() {
fmt.Println("Going down in flames!")
os.Exit(1)
}
here's how you would test it (through main_test.go
):
package main
import (
"os"
"os/exec"
"testing"
)
func TestCrasher(t *testing.T) {
if os.Getenv("BE_CRASHER") == "1" {
Crasher()
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() {
return
}
t.Fatalf("process ran with err %v, want exit status 1", err)
}
What the code does is invoke go test
again in a separate process through exec.Command
, limiting execution to the TestCrasher
test (via the -test.run=TestCrasher
switch). It also passes in a flag via an environment variable (BE_CRASHER=1
) which the second invocation checks for and, if set, calls the system-under-test, returning immediately afterwards to prevent running into an infinite loop. Thus, we are being dropped back into our original call site and may now validate the actual exit code.
Source: Slide 23 of Andrew's presentation. The second slide contains a link to the presentation's video as well.
He talks about subprocess tests at 47:09
Exit
"at the end of the world" (main
), like this pattern, then you won't be stuck writing such painful tests as the (good) accepted solution here. I fully acknowledge you may have been stuck testing someone else's code you couldn't readily refactor, but just hoping the advice is helpful to future readers… – Stutzgexec
package that is nice for testing results of executables in a black box manner. – Stutz