Skip to content

proposal: testing: report failing test after panic #32121

Description

@neild

When a test panics in a table-driven test, it is difficult to tell which case caused the failure. For example:

func Test(t *testing.T) {
        for _, test := range []int{1, 2, 0, 3} {
                t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
                        _ = 1 / test
                })
        }
}
panic: runtime error: integer divide by zero [recovered]
        panic: runtime error: integer divide by zero

goroutine 8 [running]:
testing.tRunner.func1(0xc0000ac400)
        /usr/lib/google-golang/src/testing/testing.go:830 +0x388
panic(0x511aa0, 0x623f00)
        /usr/lib/google-golang/src/runtime/panic.go:522 +0x1b5
_/usr/local/google/home/dneil/src/testpanic_test.Test.func1(0xc0000ac400)
        /usr/local/google/home/dneil/src/testpanic/panic_test.go:11 +0x24
testing.tRunner(0xc0000ac400, 0xc00005c490)
        /usr/lib/google-golang/src/testing/testing.go:865 +0xc0
created by testing.(*T).Run
        /usr/lib/google-golang/src/testing/testing.go:916 +0x357
FAIL    _/usr/local/google/home/dneil/src/testpanic     0.018s

We know that testpanic_test.Test panicked, but we don't know which test case is to blame. You can get a much more useful error by manually recovering the panic and converting it to a t.Fatal:

func Test(t *testing.T) {
        for _, test := range []int{1, 2, 0, 3} {
                t.Run(fmt.Sprintf("%v", test), func(t *testing.T) {
                        defer func() {
                                if err := recover(); err != nil {
                                        t.Error(string(debug.Stack()))
                                        t.Fatal(err)
                                }
                        }()
                        _ = 1 / test
                })
        }
}
--- FAIL: Test (0.00s)
    --- FAIL: Test/0 (0.00s)
        panic_test.go:14: runtime error: integer divide by zero
            
            goroutine 8 [running]:
            runtime/debug.Stack(0xc000046670, 0x512aa0, 0x625f00)
                /usr/lib/google-golang/src/runtime/debug/stack.go:24 +0x9d
            _/usr/local/google/home/dneil/src/testpanic_test.Test.func1.1(0xc0000ac400)
                /usr/local/google/home/dneil/src/testpanic/panic_test.go:14 +0x57
            panic(0x512aa0, 0x625f00)
                /usr/lib/google-golang/src/runtime/panic.go:522 +0x1b5
            _/usr/local/google/home/dneil/src/testpanic_test.Test.func1(0xc0000ac400)
                /usr/local/google/home/dneil/src/testpanic/panic_test.go:17 +0x7c
            testing.tRunner(0xc0000ac400, 0xc00005c490)
                /usr/lib/google-golang/src/testing/testing.go:865 +0xc0
            created by testing.(*T).Run
                /usr/lib/google-golang/src/testing/testing.go:916 +0x35a
FAIL
FAIL    _/usr/local/google/home/dneil/src/testpanic     0.016s

I propose that the testing package do something like that automatically. Perhaps rather than converting the panic to a t.Fatal it should print the failing test name and continue panicking; the important point is to provide information about which test case triggered the panic.

This doesn't help with panics in goroutines started by a test, of course.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions