-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
TestMain
is expected to call os.Exit
itself. This leads to subtle issues with defer
and panic
that leads to much headscratching. I'm proposing that if TestMain
doesn't call os.Exit
, the testing framework will call it for you after TestMain
returns.
Let's say you start here:
func TestMain(m *testing.M) {
setup()
defer teardown()
os.Exit(m.Run())
}
Subtle bug #1: your teardown isn't running; you're making a mess of your test environment. Try this:
func TestMain(m *testing.M) {
var exitCode int
defer os.Exit(exitCode)
setup()
defer teardown()
exitCode = m.Run()
}
Oops, setup has a panic
call. Now when you run go test, it's silent..
go test .
ok _/Users/abuchanan/scratch/testmain_bug 0.010s
Uh, headscratch. "Why isn't go running my tests?", you ask. Oh, os.Exit
is hiding my panic call. Ok, finally:
func TestMain(m *testing.M) {
// testMain wrapper is needed to support defers and panics.
// os.Exit will ignore those and exit silently.
os.Exit(testMain(m))
}
func testMain(m *testing.M) int {
setup()
defer teardown()
return m.Run()
}
Let's save people some headscratching (and time and effort and silly wrapper code). The testing framework probably has enough information to call os.Exit
appropriately for you after TestMain returns, right? Why require users to call it?
TestMain is really useful for writing end-to-end tests, where a single setup/teardown for all the tests is important. If I break those tests into packages for organization, it would be nice not to copy that TestMain
wrapper code everywhere.