Go's standard library includes everything you need for testing:
the testing package plus the go test command.
Where tests live
- Tests live in files named
*_test.gonext to the code they test. - They belong to the same package, or
<pkg>_testfor "black-box" tests that may only use the exported API. - They are not compiled into your normal binary.
A first test
Source strings.go (in examples/11_testing/):
package strs
func Reverse(s string) string {
rs := []rune(s)
for i, j := 0, len(rs)-1; i < j; i, j = i+1, j-1 {
rs[i], rs[j] = rs[j], rs[i]
}
return string(rs)
}
Test strings_test.go:
package strs
import "testing"
func TestReverse(t *testing.T) {
got := Reverse("hello")
want := "olleh"
if got != want {
t.Errorf("Reverse(\"hello\") = %q, want %q", got, want)
}
}
Run:
go test ./...
go test -run TestReverse ./part1-language/examples/11_testing
go test -v ./part1-language/examples/11_testing
Table-driven tests (idiomatic)
func TestReverseTable(t *testing.T) {
cases := []struct {
name, in, want string
}{
{"empty", "", ""},
{"ascii", "abc", "cba"},
{"unicode", "héllo", "olléh"},
{"palindrome", "level", "level"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := Reverse(tc.in); got != tc.want {
t.Errorf("Reverse(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}
t.Run creates subtests that show up individually:
--- PASS: TestReverseTable (0.00s)
--- PASS: TestReverseTable/empty (0.00s)
--- PASS: TestReverseTable/ascii (0.00s)
...
You can run a single subtest:
go test -run TestReverseTable/unicode ./...
Helpers and t API
| Method | What it does |
|---|---|
t.Error(args...)/t.Errorf(...) |
Mark failed, continue. |
t.Fatal(args...)/t.Fatalf(...) |
Mark failed and stop this test/subtest. |
t.Log(args...) / t.Logf(...) |
Print only when -v or test fails. |
t.Skip(...) / t.Skipf(...) |
Skip the test. |
t.Helper() |
Mark a function as a helper so failure lines point to caller. |
t.Cleanup(fn) |
Register code to run when the test ends. |
t.TempDir() |
Create a fresh temp dir; auto-removed after test. |
t.Parallel() |
Run this test in parallel with others marked. |
Benchmarks
func BenchmarkReverse(b *testing.B) {
s := "hello, world"
b.ResetTimer()
for i := 0; i < b.N; i++ {
Reverse(s)
}
}
Run:
go test -bench=. -benchmem ./...
b.N is chosen by the runtime to give a stable measurement.
Examples (also act as docs)
A function named ExampleXxx is run as a test and its output is checked
against the trailing comment:
func ExampleReverse() {
fmt.Println(Reverse("abc"))
// Output: cba
}
pkg.go.dev and go doc will display these alongside the function.
Fuzzing (Go 1.18+)
func FuzzReverse(f *testing.F) {
f.Add("hello")
f.Fuzz(func(t *testing.T, s string) {
rev := Reverse(Reverse(s))
if rev != s {
t.Errorf("Reverse(Reverse(%q)) = %q", s, rev)
}
})
}
Run:
go test -fuzz=FuzzReverse -fuzztime=10s ./...
The fuzzer mutates f.Add seeds and saves any failing input to
testdata/fuzz/... for regression.
Coverage
go test -cover ./...
go test -coverprofile=cover.out ./...
go tool cover -html=cover.out
Tips for good Go tests
- Prefer table-driven tests; they scale and document behaviour.
- Test the exported API, not internals (use
<pkg>_testpackage). - Never share state between tests; use
t.Cleanupto tear down. - Use
t.Parallel()in CPU-bound tests, but be aware of shared globals. - Keep test assertions explicit; prefer
if got != want { t.Errorf(...) }over heavyweight assertion libraries (community is split, but the standard library way is great).
Run the example tests
go test ./part1-language/examples/11_testing