I recently was working on debugging a unit test I wrote in Go. I couldn’t figure out why one of my test cases was causing a runtime error that never happened when running my actual program. I was using a runtime directive so I suspected there may be some difference between doing a go test
and a go run
. I was looking through Go build mode documentation while wondering if I could step through it with a debugger. Lo and behold you can compile your tests into an ELF executable!
Let’s take a look at this esoteric example (inspired by Dave Cheney):
package switchers
func SwitchFunction(a, b int, c *int) string {
switch *c {
case a:
return "a"
case b:
return "b"
default:
return "c"
}
}
package switchers
import "testing"
func TestSwitch(t *testing.T) {
var (
a int
b int
c = &b
)
x := SwitchFunction(a, b, c)
if x != "c" {
t.Error("wtf?")
}
}
We can compile a test binary with go test -c
. As it turns out, the go test
command is completely configurable with all linker, loader, and runtime flags. For example, you can change your GOOS
and GOARCH
environment variables to compile different test files.
You can then go ahead and run that binary through a debugger, such as delve with dlv exec switchers.test
You’re going to want to set a breakpoint for the unit test you’re trying to debug and continue to it:
From there you can step through your test one line (step
) or instruction (stepi
) at a time. Try out this example and see if you can figure out what’s going on! Find the example code here.