Reputation: 683
I am writing tests for an interpreter that panics for a number of syntactically valid input. This is a minimal example of how I do it:
package minimalexample
import (
"testing"
)
func access(i int) string {
return []string{"a", "b"}[i]
}
func TestAccess(t *testing.T) {
tests := []struct {
testdatum int
expected string
}{
{0, "a"},
{2, ""},
{1, "c"},
{4, ""},
}
for _, tt := range tests {
testAccess(tt, t)
}
}
func testAccess(tt struct {
testdatum int
expected string
}, t *testing.T) {
defer func() {
if err := recover(); err != nil {
t.Errorf("Runtime error for testdatum %v: %q", tt.testdatum, err)
}
}()
result := access(tt.testdatum)
if result != tt.expected {
t.Errorf("%v evaluates to %q, though it should be %q", tt.testdatum, result, tt.expected)
}
}
The idea to handle panic is taken from https://golang.org/doc/effective_go#recover, however I do not know whether it is good practice to use it in tests like I do in the above example or there is some reason why it is to be preferred to just let the test fail until the runtime error is fixed.
The advantage of this setting is that I see what happens for all testcases, even if one of then causes panic. The reason that I am still hesitant to say that is a good solution is that I am unsure whether it is the tester's job to care about runtime errors or whether that should be dealt with by the developer that has to fix the code to make the tests pass. Another reason is that after running a bunch of these tests, I get a msg from vscode that I don't understand but suspect to have something to do with the exhaustive deferring: "Running the contributed command: '_vscode_delegate_cmd_kms3v2s2' failed."
Upvotes: 0
Views: 686
Reputation: 8395
I don't think there's anything wrong with recovering panic in a test to catch it more cleanly, except that it is possibly indicative of a poor design pattern where you (or the package author) are using panic
for normal error handling.
See https://go-proverbs.github.io/
Don't panic.
This isn't an actual rule, but let's invent a new proverb to illustrate a point:
10 errors for every panic, 10 panics for every recover.
(looking at the GitHub search results we'll see that this is vaguely accurate)
The point being panics should be used sparingly, as often an error is more appropriate. And recover should be used ever more sparingly, because panic means something went so wrong that it's unlikely there's any way to fix it.
As a result, you should never use a panic with the intent that it should be recovered under typical use.
Again this is not a rule, it is just my philosophy about error handling in Go
panic
s should generally be reserved for particular circumstances. There could be more, but here are the main 2 in my view.
nil
pointerThe main, legitimate use of recover
is for process isolation. For example, with the standard library HTTP server, if the goroutine handling an HTTP request panics, the server will recover the panic in order to let that request crash alone, and allow the server process to keep running.
Upvotes: 1