paulgorman.org/technical

Go Testing

Go includes a testing package that works with the go test command.

Create a test file with a name ending in _test.go. The _test.go file should contain functions named like TestMyfunc with the signature:

func TestMyfunc(t *testing.T)

If the test function calls a failure function like t.Error or t.Fail, Go considers the test failed.

For a package named stringutil with a Reverse function, create a stringutil_test.go file:

package stringutil
import "testing"
func TestReverse(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"Hello, world", "dlrow ,olleH"},
		{"Hello, 世界", "界世 ,olleH"},
		{"", ""},
	}
	for _, c := range cases {
		got := Reverse(c.in)
		if got != c.want {
			t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

…and run the test:

$ go test example.com/stringutil

See how much of the project we’ve covered with tests:

$ go test -cover

Table-Driven Tests

A commonly used pattern is table-driven tests. Supply a struct matching inputs with expected return values.

func TestFib(t *testing.T) {
	tests := []struct {
		input int
		want  int
	}{
		{1, 1},
		{2, 1},
		{3, 2},
		{4, 3},
		{5, 5},
		{6, 8},
		{7, 13},
	}
	for _, test := range tests {
		got := Fib(test.input)
		if got != test.want {
			t.Errorf("Fib(%d) got %d, want %d", test.input, got, test.want)
		}
	}
}

Testing Main

What if the project is too small to break into different packages? How do we test main?

$ ls
main.go     main_test.go
$ go test *.go
ok      command-line-arguments  0.003s

Sun Oct 1 11:43:57 EDT 2017