Debugging go test

back

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.

  • compile a test binary:
    • go test -c
      • compile the tests using the -c flag:
        • -c - compile the test binary to pkg.test but do not run it (where pkg is the last element of the package’s import path). The file name can be changed with the -o flag.
    • go test -c -- switchers_test.go
  • run that binary through a debugger
    • with a compiled file:
      • dlv exec switchers.test
    • without a compiled file:
      • dlv test -- switchers_test.go
  • to run a single unit test:
    • dlv test -- -test.run ^TestSwitchC$
      • pass -test.run to select tests to run (just like go test -run)
      • once the debugger builds the test binary and launches, then set breakpoints relative to the start of the function.
      • witin the go-vim plugin allows the same functionality using the :GoDebugTestFunc command with the cursor on the required function.
  • to print the full output even for passing package tests
    • go test -v
    • dlv test -- -test.v
go mod init switchers
go mod tidy
// switchers.go
package switchers

func SwitchFunction(a, b int, c *int) string {
  switch *c {
  case a:
    return "a"
  case b:
    return "b"
  default:
    return "c"
  }
}
// switchers_test.go
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?")
  }
}

func TestSwitchC(t *testing.T) {
  var (
    a int
    b int
    c int = 8
  )

  x := SwitchFunction(a, b, &c)
  if x != "c" {
    t.Error("wtf?")
  }
}
OUTPUT
--- FAIL: TestSwitch (0.00s)
    switchers_test.go:17: wtf?
FAIL
exit status 1
FAIL    switchers       1.358s

Setting breakpoints

To debug a specific test, it’s needed to set a breakpoint on that test function.

To print list of functions.

funcs [<regex>]

Discover the test functions with the following command:

(dlv) funcs switchers.*

With the provided list of test on hand, then set a breakpoint on a specific test:

(dlv) break switchers.TestSwitch
(dlv) break switchers.SwitchFunction

To print all local variables:

(dlv) locals

To print a specific local, use the print (p) command:

p a
p b
p c
p x

Testing libraries

Most of the developers use the Go standard testing library only, but the Go ecosystem provides numerous testing libraries each with an advantage and special attributes of its own.

  • Testify: Testify is the most popular assertion and mocking toolkit for Go. It can be seamlessly integrated with the Go standard testing library and this improves readability of the test code.
  • Gocheck: Gocheck provides similar functionality to Testify. It is an assertion helper library for Go. It also supports fixture callbacks and test suite definitions.
  • Gopwt: It is a power assertion type library. It is quite difficult to identify the cause of a failed assertion and may require further debugging. A power assertion library like Gopwt helps to provide the evaluated result as part of the error message for each part of the equation.
  • Go-Testdeep: It is a deep comparison helper library.
  • Ginkgo and Gomega: It is a heavyweight BDD(behavior driven development) testing framework with assertion helpers.
  • Goconvey: Behavior Driven Development (BDD) testing library with a web user interface.
  • Goblin: A less popular BDD testing library.